diff --git a/canopen/sdo/client.py b/canopen/sdo/client.py index 82ee0c88..5fce24ff 100644 --- a/canopen/sdo/client.py +++ b/canopen/sdo/client.py @@ -83,7 +83,12 @@ def read_response(self): raise SdoAbortedError(abort_code) return response - def request_response(self, sdo_request): + def request_response(self, sdo_request, *, index=0, subindex=0): + """Send a request, identifying its transfer in any timeout abort. + + Stream operations supply the original object address. Low-level callers + can provide it explicitly; omitted values retain the zero-address default. + """ retries_left = self.MAX_RETRIES if not self.responses.empty(): # logger.warning("There were unexpected messages in the queue") @@ -96,15 +101,25 @@ def request_response(self, sdo_request): except SdoCommunicationError as e: retries_left -= 1 if not retries_left: - self.abort(ABORT_TIMED_OUT) + self.abort(ABORT_TIMED_OUT, index=index, subindex=subindex) raise logger.warning(str(e)) - def abort(self, abort_code=ABORT_GENERAL_ERROR): - """Abort current transfer.""" + def abort(self, abort_code=ABORT_GENERAL_ERROR, *, index=0, subindex=0): + """Abort a transfer with its object dictionary address. + + :param int abort_code: + Reason for aborting. Defaults to a general error. + :param int index: + Object dictionary index of the transfer, or zero if unspecified. + :param int subindex: + Object dictionary subindex of the transfer, or zero if unspecified. + + Stream operations supply their original address automatically. Existing + low-level calls without an address retain their zero-address behavior. + """ request = bytearray(8) - request[0] = REQUEST_ABORTED - # TODO: Is it necessary to include index and subindex? + SDO_STRUCT.pack_into(request, 0, REQUEST_ABORTED, index, subindex) struct.pack_into("> 1) & 0x7) if res_command & NO_MORE_DATA: @@ -348,6 +381,8 @@ def __init__(self, sdo_client, index, subindex=0, size=None, force_segment=False :param bool force_segment: Force use of segmented transfer regardless of size. """ + self._index = index + self._subindex = subindex self.sdo_client = sdo_client self.size = size self.pos = 0 @@ -363,10 +398,18 @@ def __init__(self, sdo_client, index, subindex=0, size=None, force_segment=False command |= SIZE_SPECIFIED struct.pack_into("> 2) & 0x7 @@ -641,6 +726,8 @@ def __init__(self, sdo_client, index, subindex=0, size=None, request_crc_support :param bool request_crc_support: If crc calculation should be requested when using block transfer """ + self._index = index + self._subindex = subindex self.sdo_client = sdo_client self.size = size self.pos = 0 @@ -662,15 +749,23 @@ def __init__(self, sdo_client, index, subindex=0, size=None, request_crc_support else: logger.warning("Data size has not been specified") SDO_STRUCT.pack_into(request, 0, command, index, subindex) - response = sdo_client.request_response(request) + response = sdo_client.request_response( + request, + index=self._index, + subindex=self._subindex, + ) res_command, res_index, res_subindex = SDO_STRUCT.unpack_from(response) if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD: - self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER) + self.sdo_client.abort( + ABORT_INVALID_COMMAND_SPECIFIER, + index=self._index, + subindex=self._subindex, + ) raise SdoCommunicationError( f"Unexpected response 0x{res_command:02X}") # Check that the message is for us if res_index != index or res_subindex != subindex: - self.sdo_client.abort() + self.sdo_client.abort(index=self._index, subindex=self._subindex) raise SdoCommunicationError( f"Node returned a value for {pretty_index(res_index, res_subindex)} instead, " "maybe there is another SDO client communicating " @@ -750,15 +845,23 @@ def _block_ack(self): try: response = self.sdo_client.read_response() except SdoCommunicationError: - self.sdo_client.abort(ABORT_TIMED_OUT) + self.sdo_client.abort(ABORT_TIMED_OUT, index=self._index, subindex=self._subindex) raise res_command, ackseq, blksize = struct.unpack_from("BBB", response) if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD: - self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER) + self.sdo_client.abort( + ABORT_INVALID_COMMAND_SPECIFIER, + index=self._index, + subindex=self._subindex, + ) raise SdoCommunicationError( f"Unexpected response 0x{res_command:02X}") if res_command & 0x3 != BLOCK_TRANSFER_RESPONSE: - self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER) + self.sdo_client.abort( + ABORT_INVALID_COMMAND_SPECIFIER, + index=self._index, + subindex=self._subindex, + ) raise SdoCommunicationError("Server did not respond with a " "block download response") if ackseq != self._blksize: @@ -813,7 +916,11 @@ def close(self): # Add CRC struct.pack_into("