Skip to content

Commit 28c9ed8

Browse files
committed
Disregard OD variable description in SdoClient.upload().
The upload method should behave as a raw producer of byte data. Interpretation of the returned data based on the Object Dictionary is the responsibility of the SdoVariable class, which delegates to the generic ODVariable decoding functions. Move the data truncation to the method SdoVariable.get_data(), where access to the ODVariable is certain. The only truncation that still happens is based on the response size specified by the server, which might be smaller for e.g. expedited upload. Extend the upload() function docstring to clarify the changed behavior. Adjust the test case for expedited upload with unspecified size in the response, which is the only incompatible change.
1 parent 66d0bb2 commit 28c9ed8

File tree

3 files changed

+20
-14
lines changed

3 files changed

+20
-14
lines changed

canopen/sdo/base.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,18 @@ def __init__(self, sdo_node: SdoBase, od: objectdictionary.ODVariable):
148148
variable.Variable.__init__(self, od)
149149

150150
def get_data(self) -> bytes:
151-
return self.sdo_node.upload(self.od.index, self.od.subindex)
151+
data = self.sdo_node.upload(self.od.index, self.od.subindex)
152+
response_size = len(data)
153+
154+
# If size is available through variable in OD, then use the smaller of the two sizes.
155+
# Some devices send U32/I32 even if variable is smaller in OD
156+
if self.od.fixed_size:
157+
# Get the size in bytes for this variable
158+
var_size = len(self.od) // 8
159+
if response_size is None or var_size < response_size:
160+
# Truncate the data to specified size
161+
data = data[0:var_size]
162+
return data
152163

153164
def set_data(self, data: bytes):
154165
force_segment = self.od.data_type == objectdictionary.DOMAIN

canopen/sdo/client.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ def abort(self, abort_code=0x08000000):
105105
def upload(self, index: int, subindex: int) -> bytes:
106106
"""May be called to make a read operation without an Object Dictionary.
107107
108+
No validation against the Object Dictionary is performed, even if an object description
109+
would be available. The length of the returned data depends only on the transferred
110+
amount, possibly truncated to the size indicated by the server.
111+
108112
:param index:
109113
Index of object to read.
110114
:param subindex:
@@ -121,17 +125,8 @@ def upload(self, index: int, subindex: int) -> bytes:
121125
response_size = fp.size
122126
data = fp.read()
123127

124-
# If size is available through variable in OD, then use the smaller of the two sizes.
125-
# Some devices send U32/I32 even if variable is smaller in OD
126-
var = self.od.get_variable(index, subindex)
127-
if var is not None:
128-
# Found a matching variable in OD
129-
if var.fixed_size:
130-
# Get the size in bytes for this variable
131-
var_size = len(var) // 8
132-
if response_size is None or var_size < response_size:
133-
# Truncate the data to specified size
134-
data = data[0:var_size]
128+
if response_size and response_size < len(data):
129+
data = data[:response_size]
135130
return data
136131

137132
def download(

test/test_sdo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ def test_size_not_specified(self):
102102
(TX, b'\x40\x00\x14\x02\x00\x00\x00\x00'),
103103
(RX, b'\x42\x00\x14\x02\xfe\x00\x00\x00')
104104
]
105-
# Make sure the size of the data is 1 byte
105+
# This method used to truncate to 1 byte, but returns raw content now
106106
data = self.network[2].sdo.upload(0x1400, 2)
107-
self.assertEqual(data, b'\xfe')
107+
self.assertEqual(data, b'\xfe\x00\x00\x00')
108108
self.assertTrue(self.message_sent)
109109

110110
def test_expedited_download(self):

0 commit comments

Comments
 (0)