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

Support partial usbtmc reads and request the size amount of bytes from the device #470

Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ PyVISA-py Changelog
has been read (see specification), and only expects a header on the first packet received.
- fix usbtmc implementation to properly discard the alignment bytes
ensuring only the actual data (`transfer_size`) is retained in the message PR #465
- Implemented partial USBTMC message functionality that allows reading the amount of bytes
specified by host PR #470
- add support for VI_ATTR_SUPPRESS_END_EN for USB resources PR #449

0.7.2 (07/03/2024)
Expand Down
73 changes: 48 additions & 25 deletions pyvisa_py/protocols/usbtmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""

import enum
import math

Check warning on line 15 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L15

Added line #L15 was not covered by tests
import struct
import time
import warnings
Expand Down Expand Up @@ -455,13 +456,7 @@
return size

def read(self, size):
header_size = 12
max_padding = 511
recv_chunk = self.usb_recv_ep.wMaxPacketSize - header_size

if size > 0 and size < recv_chunk:
recv_chunk = size

usbtmc_header_size = 12

Check warning on line 459 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L459

Added line #L459 was not covered by tests
eom = False

raw_read = super(USBTMC, self).read
Expand All @@ -473,33 +468,61 @@
received_transfer = bytearray()
self._btag = (self._btag % 255) + 1

req = BulkInMessage.build_array(self._btag, recv_chunk, None)

req = BulkInMessage.build_array(self._btag, size, None)

Check warning on line 471 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L471

Added line #L471 was not covered by tests
raw_write(req)

try:
resp = raw_read(recv_chunk + header_size + max_padding)
# make sure the data request is in multitudes of wMaxPacketSize.
# + 1 * wMaxPacketSize for message sizes that equals wMaxPacketSize == size + usbtmc_header_size.
# This to be able to retrieve a short package to end communication
# (see USB 2.0 Section 5.8.3 and USBTMC Section 3.3)
chunk_size = (

Check warning on line 479 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L479

Added line #L479 was not covered by tests
math.floor(
(size + usbtmc_header_size) / self.usb_recv_ep.wMaxPacketSize
)
+ 1
) * self.usb_recv_ep.wMaxPacketSize
resp = raw_read(chunk_size)

Check warning on line 485 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L485

Added line #L485 was not covered by tests

response = BulkInMessage.from_bytes(resp)
received_transfer.extend(response.data)
while (
len(resp) == self.usb_recv_ep.wMaxPacketSize
or len(received_transfer) < response.transfer_size
):
# USBTMC Section 3.3 specifies that the first usb packet
# must contain the header. the remaining packets do not need
# the header the message is finished when a "short packet"
# is sent (one whose length is less than wMaxPacketSize)
# wMaxPacketSize may be incorrectly reported by certain drivers.
# Therefore, continue reading until the transfer_size is reached.
resp = raw_read(recv_chunk + header_size + max_padding)
received_transfer.extend(resp)

# Detect EOM only when device sends all expected bytes.
if len(received_transfer) >= response.transfer_size:
eom = response.transfer_attributes & 1
# Truncate data to the specified length (discard padding)
# USBTMC header (12 bytes) has already truncated
received_message.extend(received_transfer[: response.transfer_size])
if not eom and len(received_transfer) >= size:
# Read asking for 'size' bytes from the device.
# This may be less then the device wants to send back in a message
# Therefore the request does not mean that we must receive a EOM.
# Multiple `transfers` will be required to retrieve the remaining bytes.
eom = True

Check warning on line 498 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L498

Added line #L498 was not covered by tests
else:
while (
(len(resp) % self.usb_recv_ep.wMaxPacketSize) == 0
or len(received_transfer) < response.transfer_size
) and not eom:
# USBTMC Section 3.3 specifies that the first usb packet
# must contain the header. the remaining packets do not need
# the header the message is finished when a "short packet"
# is sent (one whose length is less than wMaxPacketSize)
# wMaxPacketSize may be incorrectly reported by certain drivers.
# Therefore, continue reading until the transfer_size is reached.
chunk_size = (

Check warning on line 510 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L510

Added line #L510 was not covered by tests
math.floor(
(size - len(received_transfer))
/ self.usb_recv_ep.wMaxPacketSize
)
+ 1
) * self.usb_recv_ep.wMaxPacketSize
resp = raw_read(chunk_size)
received_transfer.extend(resp)

Check warning on line 518 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L517-L518

Added lines #L517 - L518 were not covered by tests
if len(received_transfer) >= response.transfer_size:
eom = response.transfer_attributes & 1

Check warning on line 520 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L520

Added line #L520 was not covered by tests
if not eom and len(received_transfer) >= size:
eom = True

Check warning on line 522 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L522

Added line #L522 was not covered by tests
# Truncate data to the specified length (discard padding)
# USBTMC header (12 bytes) has already truncated
received_message.extend(received_transfer[: response.transfer_size])

Check warning on line 525 in pyvisa_py/protocols/usbtmc.py

View check run for this annotation

Codecov / codecov/patch

pyvisa_py/protocols/usbtmc.py#L525

Added line #L525 was not covered by tests
except (usb.core.USBError, ValueError):
# Abort failed Bulk-IN operation.
self._abort_bulk_in(self._btag)
Expand Down
Loading