Skip to content

Commit 4990572

Browse files
authored
sdo: Define constants for all standardized abort codes (#596)
* Define constants for all standardized SDO abort codes. * Type annotations for constants (Final). * Use constants in SDO client and server. * Keep literals in tests, but comment and format.
1 parent d35ce74 commit 4990572

File tree

4 files changed

+61
-26
lines changed

4 files changed

+61
-26
lines changed

canopen/sdo/client.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ def request_response(self, sdo_request):
9696
except SdoCommunicationError as e:
9797
retries_left -= 1
9898
if not retries_left:
99-
self.abort(0x0504_0000)
99+
self.abort(ABORT_TIMED_OUT)
100100
raise
101101
logger.warning(str(e))
102102

103-
def abort(self, abort_code=0x0800_0000):
103+
def abort(self, abort_code=ABORT_GENERAL_ERROR):
104104
"""Abort current transfer."""
105105
request = bytearray(8)
106106
request[0] = REQUEST_ABORTED
@@ -309,10 +309,10 @@ def read(self, size=-1):
309309
response = self.sdo_client.request_response(request)
310310
res_command, = struct.unpack_from("B", response)
311311
if res_command & 0xE0 != RESPONSE_SEGMENT_UPLOAD:
312-
self.sdo_client.abort(0x0504_0001)
312+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
313313
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
314314
if res_command & TOGGLE_BIT != self._toggle:
315-
self.sdo_client.abort(0x0503_0000)
315+
self.sdo_client.abort(ABORT_TOGGLE_NOT_ALTERNATED)
316316
raise SdoCommunicationError("Toggle bit mismatch")
317317
length = 7 - ((res_command >> 1) & 0x7)
318318
if res_command & NO_MORE_DATA:
@@ -371,7 +371,7 @@ def __init__(self, sdo_client, index, subindex=0, size=None, force_segment=False
371371
response = sdo_client.request_response(request)
372372
res_command, = struct.unpack_from("B", response)
373373
if res_command != RESPONSE_DOWNLOAD:
374-
self.sdo_client.abort(0x0504_0001)
374+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
375375
raise SdoCommunicationError(
376376
f"Unexpected response 0x{res_command:02X}")
377377
else:
@@ -400,7 +400,7 @@ def write(self, b):
400400
response = self.sdo_client.request_response(request)
401401
res_command, = struct.unpack_from("B", response)
402402
if res_command & 0xE0 != RESPONSE_DOWNLOAD:
403-
self.sdo_client.abort(0x0504_0001)
403+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
404404
raise SdoCommunicationError(
405405
f"Unexpected response 0x{res_command:02X}")
406406
bytes_sent = len(b)
@@ -425,7 +425,7 @@ def write(self, b):
425425
response = self.sdo_client.request_response(request)
426426
res_command, = struct.unpack("B", response[0:1])
427427
if res_command & 0xE0 != RESPONSE_SEGMENT_DOWNLOAD:
428-
self.sdo_client.abort(0x0504_0001)
428+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
429429
raise SdoCommunicationError(
430430
f"Unexpected response 0x{res_command:02X} "
431431
f"(expected 0x{RESPONSE_SEGMENT_DOWNLOAD:02X})")
@@ -499,7 +499,7 @@ def __init__(self, sdo_client, index, subindex=0, request_crc_support=True):
499499
res_command, res_index, res_subindex = SDO_STRUCT.unpack_from(response)
500500
if res_command & 0xE0 != RESPONSE_BLOCK_UPLOAD:
501501
self._error = True
502-
self.sdo_client.abort(0x0504_0001)
502+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
503503
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
504504
# Check that the message is for us
505505
if res_index != index or res_subindex != subindex:
@@ -556,7 +556,7 @@ def read(self, size=-1):
556556
if self._done:
557557
if self._server_crc != self._crc.final():
558558
self._error = True
559-
self.sdo_client.abort(0x0504_0004)
559+
self.sdo_client.abort(ABORT_CRC_ERROR)
560560
raise SdoCommunicationError("CRC is not OK")
561561
logger.info("CRC is OK")
562562
self.pos += len(data)
@@ -576,7 +576,7 @@ def _retransmit(self):
576576
self._ackseq = seqno
577577
return response
578578
self._error = True
579-
self.sdo_client.abort(0x0504_0000)
579+
self.sdo_client.abort(ABORT_TIMED_OUT)
580580
raise SdoCommunicationError("Some data was lost and could not be retransmitted")
581581

582582
def _ack_block(self):
@@ -591,16 +591,16 @@ def _end_upload(self):
591591
try:
592592
response = self.sdo_client.read_response()
593593
except SdoCommunicationError:
594-
self.abort(0x0504_0000)
594+
self.abort(ABORT_TIMED_OUT)
595595
raise
596596
res_command, self._server_crc = struct.unpack_from("<BH", response)
597597
if res_command & 0xE0 != RESPONSE_BLOCK_UPLOAD:
598598
self._error = True
599-
self.sdo_client.abort(0x0504_0001)
599+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
600600
raise SdoCommunicationError(f"Unexpected response 0x{res_command:02X}")
601601
if res_command & 0x3 != END_BLOCK_TRANSFER:
602602
self._error = True
603-
self.sdo_client.abort(0x0504_0001)
603+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
604604
raise SdoCommunicationError("Server did not end transfer as expected")
605605
# Return number of bytes not used in last message
606606
return (res_command >> 2) & 0x7
@@ -670,7 +670,7 @@ def __init__(self, sdo_client, index, subindex=0, size=None, request_crc_support
670670
response = sdo_client.request_response(request)
671671
res_command, res_index, res_subindex = SDO_STRUCT.unpack_from(response)
672672
if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD:
673-
self.sdo_client.abort(0x0504_0001)
673+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
674674
raise SdoCommunicationError(
675675
f"Unexpected response 0x{res_command:02X}")
676676
# Check that the message is for us
@@ -755,15 +755,15 @@ def _block_ack(self):
755755
try:
756756
response = self.sdo_client.read_response()
757757
except SdoCommunicationError:
758-
self.sdo_client.abort(0x0504_0000)
758+
self.sdo_client.abort(ABORT_TIMED_OUT)
759759
raise
760760
res_command, ackseq, blksize = struct.unpack_from("BBB", response)
761761
if res_command & 0xE0 != RESPONSE_BLOCK_DOWNLOAD:
762-
self.sdo_client.abort(0x0504_0001)
762+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
763763
raise SdoCommunicationError(
764764
f"Unexpected response 0x{res_command:02X}")
765765
if res_command & 0x3 != BLOCK_TRANSFER_RESPONSE:
766-
self.sdo_client.abort(0x0504_0001)
766+
self.sdo_client.abort(ABORT_INVALID_COMMAND_SPECIFIER)
767767
raise SdoCommunicationError("Server did not respond with a "
768768
"block download response")
769769
if ackseq != self._blksize:

canopen/sdo/constants.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import struct
2+
from typing import Final
3+
24

35
# Command, index, subindex
46
SDO_STRUCT = struct.Struct("<BHB")
@@ -31,3 +33,36 @@
3133
NO_MORE_DATA = 0x1
3234
NO_MORE_BLOCKS = 0x80
3335
TOGGLE_BIT = 0x10
36+
37+
# Abort codes
38+
ABORT_TOGGLE_NOT_ALTERNATED: Final[int] = 0x0503_0000
39+
ABORT_TIMED_OUT: Final[int] = 0x0504_0000
40+
ABORT_INVALID_COMMAND_SPECIFIER: Final[int] = 0x0504_0001
41+
ABORT_INVALID_BLOCK_SIZE: Final[int] = 0x0504_0002
42+
ABORT_INVALID_SEQUENCE_NUMBER: Final[int] = 0x0504_0003
43+
ABORT_CRC_ERROR: Final[int] = 0x0504_0004
44+
ABORT_OUT_OF_MEMORY: Final[int] = 0x0504_0005
45+
ABORT_UNSUPPORTED_ACCESS: Final[int] = 0x0601_0000
46+
ABORT_READ_WRITEONLY: Final[int] = 0x0601_0001
47+
ABORT_WRITE_READONLY: Final[int] = 0x0601_0002
48+
ABORT_NOT_IN_OD: Final[int] = 0x0602_0000
49+
ABORT_PDO_CANNOT_MAP: Final[int] = 0x0604_0041
50+
ABORT_PDO_LENGTH_EXCEEDED: Final[int] = 0x0604_0042
51+
ABORT_PARAMETER_INCOMPATIBLE: Final[int] = 0x0604_0043
52+
ABORT_INTERNAL_INCOMPATIBILITY: Final[int] = 0x0604_0047
53+
ABORT_HARDWARE_ERROR: Final[int] = 0x0606_0000
54+
ABORT_LENGTH_NOT_MATCHED: Final[int] = 0x0607_0010
55+
ABORT_LENGTH_TOO_HIGH: Final[int] = 0x0607_0012
56+
ABORT_LENGTH_TOO_LOW: Final[int] = 0x0607_0013
57+
ABORT_NO_SUBINDEX: Final[int] = 0x0609_0011
58+
ABORT_INVALID_VALUE: Final[int] = 0x0609_0030 # download only
59+
ABORT_VALUE_TOO_HIGH: Final[int] = 0x0609_0031 # download only
60+
ABORT_VALUE_TOO_LOW: Final[int] = 0x0609_0032 # download only
61+
ABORT_MAXIMUM_LESS_THAN_MINIMUM: Final[int] = 0x0609_0036
62+
ABORT_NO_SDO_CONNECTION: Final[int] = 0x060A_0023
63+
ABORT_GENERAL_ERROR: Final[int] = 0x0800_0000
64+
ABORT_STORE_APPLICATION: Final[int] = 0x0800_0020
65+
ABORT_APPLICATION_LOCAL_CONTROL: Final[int] = 0x0800_0021
66+
ABORT_APPLICATION_DEVICE_STATE: Final[int] = 0x0800_0022
67+
ABORT_OD_GENERATION: Final[int] = 0x0800_0023
68+
ABORT_NO_DATA_AVAILABLE: Final[int] = 0x0800_0024

canopen/sdo/server.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ def on_request(self, can_id, data, timestamp):
4848
elif ccs == REQUEST_ABORTED:
4949
self.request_aborted(data)
5050
else:
51-
self.abort(0x05040001)
51+
self.abort(ABORT_INVALID_COMMAND_SPECIFIER)
5252
except SdoAbortedError as exc:
5353
self.abort(exc.code)
5454
except KeyError as exc:
55-
self.abort(0x06020000)
55+
self.abort(ABORT_NOT_IN_OD)
5656
except Exception as exc:
5757
self.abort()
5858
logger.exception(exc)
@@ -68,7 +68,7 @@ def init_upload(self, request):
6868
size = len(data)
6969
if size == 0:
7070
logger.info("No content to upload for 0x%04X:%02X", index, subindex)
71-
self.abort(0x0800_0024)
71+
self.abort(ABORT_NO_DATA_AVAILABLE)
7272
return
7373
elif size <= 4:
7474
logger.info("Expedited upload for 0x%04X:%02X", index, subindex)
@@ -87,7 +87,7 @@ def init_upload(self, request):
8787
def segmented_upload(self, command):
8888
if command & TOGGLE_BIT != self._toggle:
8989
# Toggle bit mismatch
90-
raise SdoAbortedError(0x05030000)
90+
raise SdoAbortedError(ABORT_TOGGLE_NOT_ALTERNATED)
9191
data = self._buffer[:7]
9292
size = len(data)
9393

@@ -129,7 +129,7 @@ def block_download(self, data):
129129
self._index = index
130130
self._subindex = subindex
131131
logger.error("Block download is not supported")
132-
self.abort(0x05040001)
132+
self.abort(ABORT_INVALID_COMMAND_SPECIFIER)
133133

134134
def init_download(self, request):
135135
# TODO: Check if writable (now would fail on end of segmented downloads)
@@ -160,7 +160,7 @@ def init_download(self, request):
160160
def segmented_download(self, command, request):
161161
if command & TOGGLE_BIT != self._toggle:
162162
# Toggle bit mismatch
163-
raise SdoAbortedError(0x05030000)
163+
raise SdoAbortedError(ABORT_TOGGLE_NOT_ALTERNATED)
164164
last_byte = 8 - ((command >> 1) & 0x7)
165165
self._buffer.extend(request[1:last_byte])
166166

@@ -183,7 +183,7 @@ def segmented_download(self, command, request):
183183
def send_response(self, response):
184184
self.network.send_message(self.tx_cobid, response)
185185

186-
def abort(self, abort_code=0x08000000):
186+
def abort(self, abort_code=ABORT_GENERAL_ERROR):
187187
"""Abort current transfer."""
188188
data = struct.pack("<BHBL", RESPONSE_ABORTED,
189189
self._index, self._subindex, abort_code)

test/test_local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ def test_nmt_state_initializing_to_preoper(self):
116116
self.assertEqual(state, 'PRE-OPERATIONAL')
117117

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

125125
def test_start_remote_node(self):
126126
self.remote_node.nmt.state = 'OPERATIONAL'

0 commit comments

Comments
 (0)