Skip to content

sdo: Define constants for all standardized abort codes #596

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
34 changes: 17 additions & 17 deletions canopen/sdo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@
except SdoCommunicationError as e:
retries_left -= 1
if not retries_left:
self.abort(0x0504_0000)
self.abort(ABORT_TIMED_OUT)

Check warning on line 99 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L99

Added line #L99 was not covered by tests
raise
logger.warning(str(e))

def abort(self, abort_code=0x0800_0000):
def abort(self, abort_code=ABORT_GENERAL_ERROR):
"""Abort current transfer."""
request = bytearray(8)
request[0] = REQUEST_ABORTED
Expand Down Expand Up @@ -309,10 +309,10 @@
response = self.sdo_client.request_response(request)
res_command, = struct.unpack_from("B", response)
if res_command & 0xE0 != RESPONSE_SEGMENT_UPLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 312 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L312

Added line #L312 was not covered by tests
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
if res_command & TOGGLE_BIT != self._toggle:
self.sdo_client.abort(0x0503_0000)
self.sdo_client.abort(ABORT_TOGGLE_NOT_ALTERNATED)

Check warning on line 315 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L315

Added line #L315 was not covered by tests
raise SdoCommunicationError("Toggle bit mismatch")
length = 7 - ((res_command >> 1) & 0x7)
if res_command & NO_MORE_DATA:
Expand Down Expand Up @@ -371,7 +371,7 @@
response = sdo_client.request_response(request)
res_command, = struct.unpack_from("B", response)
if res_command != RESPONSE_DOWNLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 374 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L374

Added line #L374 was not covered by tests
raise SdoCommunicationError(
f"Unexpected response 0x{res_command:02X}")
else:
Expand Down Expand Up @@ -400,7 +400,7 @@
response = self.sdo_client.request_response(request)
res_command, = struct.unpack_from("B", response)
if res_command & 0xE0 != RESPONSE_DOWNLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 403 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L403

Added line #L403 was not covered by tests
raise SdoCommunicationError(
f"Unexpected response 0x{res_command:02X}")
bytes_sent = len(b)
Expand All @@ -425,7 +425,7 @@
response = self.sdo_client.request_response(request)
res_command, = struct.unpack("B", response[0:1])
if res_command & 0xE0 != RESPONSE_SEGMENT_DOWNLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 428 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L428

Added line #L428 was not covered by tests
raise SdoCommunicationError(
f"Unexpected response 0x{res_command:02X} "
f"(expected 0x{RESPONSE_SEGMENT_DOWNLOAD:02X})")
Expand Down Expand Up @@ -499,7 +499,7 @@
res_command, res_index, res_subindex = SDO_STRUCT.unpack_from(response)
if res_command & 0xE0 != RESPONSE_BLOCK_UPLOAD:
self._error = True
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
# Check that the message is for us
if res_index != index or res_subindex != subindex:
Expand Down Expand Up @@ -556,7 +556,7 @@
if self._done:
if self._server_crc != self._crc.final():
self._error = True
self.sdo_client.abort(0x0504_0004)
self.sdo_client.abort(ABORT_CRC_ERROR)

Check warning on line 559 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L559

Added line #L559 was not covered by tests
raise SdoCommunicationError("CRC is not OK")
logger.info("CRC is OK")
self.pos += len(data)
Expand All @@ -576,7 +576,7 @@
self._ackseq = seqno
return response
self._error = True
self.sdo_client.abort(0x0504_0000)
self.sdo_client.abort(ABORT_TIMED_OUT)

Check warning on line 579 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L579

Added line #L579 was not covered by tests
raise SdoCommunicationError("Some data was lost and could not be retransmitted")

def _ack_block(self):
Expand All @@ -591,16 +591,16 @@
try:
response = self.sdo_client.read_response()
except SdoCommunicationError:
self.abort(0x0504_0000)
self.abort(ABORT_TIMED_OUT)

Check warning on line 594 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L594

Added line #L594 was not covered by tests
raise
res_command, self._server_crc = struct.unpack_from("<BH", response)
if res_command & 0xE0 != RESPONSE_BLOCK_UPLOAD:
self._error = True
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 599 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L599

Added line #L599 was not covered by tests
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
if res_command & 0x3 != END_BLOCK_TRANSFER:
self._error = True
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 603 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L603

Added line #L603 was not covered by tests
raise SdoCommunicationError("Server did not end transfer as expected")
# Return number of bytes not used in last message
return (res_command >> 2) & 0x7
Expand Down Expand Up @@ -670,7 +670,7 @@
response = sdo_client.request_response(request)
res_command, res_index, res_subindex = SDO_STRUCT.unpack_from(response)
if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 673 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L673

Added line #L673 was not covered by tests
raise SdoCommunicationError(
f"Unexpected response 0x{res_command:02X}")
# Check that the message is for us
Expand Down Expand Up @@ -755,15 +755,15 @@
try:
response = self.sdo_client.read_response()
except SdoCommunicationError:
self.sdo_client.abort(0x0504_0000)
self.sdo_client.abort(ABORT_TIMED_OUT)

Check warning on line 758 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L758

Added line #L758 was not covered by tests
raise
res_command, ackseq, blksize = struct.unpack_from("BBB", response)
if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 762 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L762

Added line #L762 was not covered by tests
raise SdoCommunicationError(
f"Unexpected response 0x{res_command:02X}")
if res_command & 0x3 != BLOCK_TRANSFER_RESPONSE:
self.sdo_client.abort(0x0504_0001)
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 766 in canopen/sdo/client.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/client.py#L766

Added line #L766 was not covered by tests
raise SdoCommunicationError("Server did not respond with a "
"block download response")
if ackseq != self._blksize:
Expand Down
35 changes: 35 additions & 0 deletions canopen/sdo/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import struct
from typing import Final


# Command, index, subindex
SDO_STRUCT = struct.Struct("<BHB")
Expand Down Expand Up @@ -31,3 +33,36 @@
NO_MORE_DATA = 0x1
NO_MORE_BLOCKS = 0x80
TOGGLE_BIT = 0x10

# Abort codes
ABORT_TOGGLE_NOT_ALTERNATED: Final[int] = 0x0503_0000
ABORT_TIMED_OUT: Final[int] = 0x0504_0000
ABORT_INVALID_COMMAND_SPECIFIER: Final[int] = 0x0504_0001
ABORT_INVALID_BLOCK_SIZE: Final[int] = 0x0504_0002
ABORT_INVALID_SEQUENCE_NUMBER: Final[int] = 0x0504_0003
ABORT_CRC_ERROR: Final[int] = 0x0504_0004
ABORT_OUT_OF_MEMORY: Final[int] = 0x0504_0005
ABORT_UNSUPPORTED_ACCESS: Final[int] = 0x0601_0000
ABORT_READ_WRITEONLY: Final[int] = 0x0601_0001
ABORT_WRITE_READONLY: Final[int] = 0x0601_0002
ABORT_NOT_IN_OD: Final[int] = 0x0602_0000
ABORT_PDO_CANNOT_MAP: Final[int] = 0x0604_0041
ABORT_PDO_LENGTH_EXCEEDED: Final[int] = 0x0604_0042
ABORT_PARAMETER_INCOMPATIBLE: Final[int] = 0x0604_0043
ABORT_INTERNAL_INCOMPATIBILITY: Final[int] = 0x0604_0047
ABORT_HARDWARE_ERROR: Final[int] = 0x0606_0000
ABORT_LENGTH_NOT_MATCHED: Final[int] = 0x0607_0010
ABORT_LENGTH_TOO_HIGH: Final[int] = 0x0607_0012
ABORT_LENGTH_TOO_LOW: Final[int] = 0x0607_0013
ABORT_NO_SUBINDEX: Final[int] = 0x0609_0011
ABORT_INVALID_VALUE: Final[int] = 0x0609_0030 # download only
ABORT_VALUE_TOO_HIGH: Final[int] = 0x0609_0031 # download only
ABORT_VALUE_TOO_LOW: Final[int] = 0x0609_0032 # download only
ABORT_MAXIMUM_LESS_THAN_MINIMUM: Final[int] = 0x0609_0036
ABORT_NO_SDO_CONNECTION: Final[int] = 0x060A_0023
ABORT_GENERAL_ERROR: Final[int] = 0x0800_0000
ABORT_STORE_APPLICATION: Final[int] = 0x0800_0020
ABORT_APPLICATION_LOCAL_CONTROL: Final[int] = 0x0800_0021
ABORT_APPLICATION_DEVICE_STATE: Final[int] = 0x0800_0022
ABORT_OD_GENERATION: Final[int] = 0x0800_0023
ABORT_NO_DATA_AVAILABLE: Final[int] = 0x0800_0024
14 changes: 7 additions & 7 deletions canopen/sdo/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@
elif ccs == REQUEST_ABORTED:
self.request_aborted(data)
else:
self.abort(0x05040001)
self.abort(ABORT_INVALID_COMMAND_SPECIFIER)

Check warning on line 51 in canopen/sdo/server.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/server.py#L51

Added line #L51 was not covered by tests
except SdoAbortedError as exc:
self.abort(exc.code)
except KeyError as exc:
self.abort(0x06020000)
self.abort(ABORT_NOT_IN_OD)

Check warning on line 55 in canopen/sdo/server.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/server.py#L55

Added line #L55 was not covered by tests
except Exception as exc:
self.abort()
logger.exception(exc)
Expand All @@ -68,7 +68,7 @@
size = len(data)
if size == 0:
logger.info("No content to upload for 0x%04X:%02X", index, subindex)
self.abort(0x0800_0024)
self.abort(ABORT_NO_DATA_AVAILABLE)
return
elif size <= 4:
logger.info("Expedited upload for 0x%04X:%02X", index, subindex)
Expand All @@ -87,7 +87,7 @@
def segmented_upload(self, command):
if command & TOGGLE_BIT != self._toggle:
# Toggle bit mismatch
raise SdoAbortedError(0x05030000)
raise SdoAbortedError(ABORT_TOGGLE_NOT_ALTERNATED)

Check warning on line 90 in canopen/sdo/server.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/server.py#L90

Added line #L90 was not covered by tests
data = self._buffer[:7]
size = len(data)

Expand Down Expand Up @@ -129,7 +129,7 @@
self._index = index
self._subindex = subindex
logger.error("Block download is not supported")
self.abort(0x05040001)
self.abort(ABORT_INVALID_COMMAND_SPECIFIER)

def init_download(self, request):
# TODO: Check if writable (now would fail on end of segmented downloads)
Expand Down Expand Up @@ -160,7 +160,7 @@
def segmented_download(self, command, request):
if command & TOGGLE_BIT != self._toggle:
# Toggle bit mismatch
raise SdoAbortedError(0x05030000)
raise SdoAbortedError(ABORT_TOGGLE_NOT_ALTERNATED)

Check warning on line 163 in canopen/sdo/server.py

View check run for this annotation

Codecov / codecov/patch

canopen/sdo/server.py#L163

Added line #L163 was not covered by tests
last_byte = 8 - ((command >> 1) & 0x7)
self._buffer.extend(request[1:last_byte])

Expand All @@ -183,7 +183,7 @@
def send_response(self, response):
self.network.send_message(self.tx_cobid, response)

def abort(self, abort_code=0x08000000):
def abort(self, abort_code=ABORT_GENERAL_ERROR):
"""Abort current transfer."""
data = struct.pack("<BHBL", RESPONSE_ABORTED,
self._index, self._subindex, abort_code)
Expand Down
4 changes: 2 additions & 2 deletions test/test_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ def test_nmt_state_initializing_to_preoper(self):
self.assertEqual(state, 'PRE-OPERATIONAL')

def test_receive_abort_request(self):
self.remote_node.sdo.abort(0x05040003)
self.remote_node.sdo.abort(0x0504_0003) # Invalid sequence number
# Line below is just so that we are sure the client have received the abort
# before we do the check
time.sleep(0.1)
self.assertEqual(self.local_node.sdo.last_received_error, 0x05040003)
self.assertEqual(self.local_node.sdo.last_received_error, 0x0504_0003)

def test_start_remote_node(self):
self.remote_node.nmt.state = 'OPERATIONAL'
Expand Down
Loading