Skip to content

Commit 67420a1

Browse files
committed
Improvements
* Add __repr__ to the object classes * Rename PDO Map to PdoMap * Rename PDO Maps to PdoMaps * Fix iterator in SdoArray * Add alen() to SdoArray and SdoRecord * Add __eq__ to SdoAbordedError
1 parent 41e028d commit 67420a1

File tree

8 files changed

+62
-20
lines changed

8 files changed

+62
-20
lines changed

canopen/objectdictionary/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ def __getitem__(
109109
item = self.names.get(index) or self.indices.get(index)
110110
if item is None:
111111
if isinstance(index, str) and '.' in index:
112-
parts = index.split('.')
113-
return self[parts[0]][".".join(parts[1:])]
112+
idx, sub = index.split('.', maxsplit=1)
113+
return self[idx][sub]
114114
name = "0x%X" % index if isinstance(index, int) else index
115115
raise KeyError("%s was not found in Object Dictionary" % name)
116116
return item
@@ -182,6 +182,9 @@ def __init__(self, name: str, index: int):
182182
self.subindices = {}
183183
self.names = {}
184184

185+
def __repr__(self) -> str:
186+
return f"<{type(self).__qualname__} {self.name!r} at 0x{self.index:04x}>"
187+
185188
def __getitem__(self, subindex: Union[int, str]) -> "ODVariable":
186189
item = self.names.get(subindex) or self.subindices.get(subindex)
187190
if item is None:
@@ -238,6 +241,9 @@ def __init__(self, name: str, index: int):
238241
self.subindices = {}
239242
self.names = {}
240243

244+
def __repr__(self) -> str:
245+
return f"<{type(self).__qualname__} {self.name!r} at 0x{self.index:04x}>"
246+
241247
def __getitem__(self, subindex: Union[int, str]) -> "ODVariable":
242248
var = self.names.get(subindex) or self.subindices.get(subindex)
243249
if var is not None:
@@ -333,6 +339,15 @@ def __init__(self, name: str, index: int, subindex: int = 0):
333339
#: Can this variable be mapped to a PDO
334340
self.pdo_mappable = False
335341

342+
def __repr__(self) -> str:
343+
suffix = f":{self.subindex:02x}" if isinstance(self.parent, (ODRecord, ODArray)) else ""
344+
return f"<{type(self).__qualname__} {self.qualname!r} at 0x{self.index:04x}{suffix}>"
345+
346+
@property
347+
def qualname(self) -> str:
348+
if isinstance(self.parent, (ODRecord, ODArray)):
349+
return f"{self.parent.name}.{self.name}"
350+
return self.name
336351

337352
def __eq__(self, other: "ODVariable") -> bool:
338353
return (self.index == other.index and

canopen/pdo/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22

33
from canopen import node
4-
from canopen.pdo.base import PdoBase, Maps
4+
from canopen.pdo.base import PdoBase, PdoMaps
55

66
# Compatibility
77
from canopen.pdo.base import Variable
@@ -38,7 +38,7 @@ class RPDO(PdoBase):
3838

3939
def __init__(self, node):
4040
super(RPDO, self).__init__(node)
41-
self.map = Maps(0x1400, 0x1600, self, 0x200)
41+
self.map = PdoMaps(0x1400, 0x1600, self, 0x200)
4242
logger.debug('RPDO Map as {0}'.format(len(self.map)))
4343

4444
def stop(self):
@@ -63,7 +63,7 @@ class TPDO(PdoBase):
6363

6464
def __init__(self, node):
6565
super(TPDO, self).__init__(node)
66-
self.map = Maps(0x1800, 0x1A00, self, 0x180)
66+
self.map = PdoMaps(0x1800, 0x1A00, self, 0x180)
6767
logger.debug('TPDO Map as {0}'.format(len(self.map)))
6868

6969
def stop(self):

canopen/pdo/base.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class PdoBase(Mapping):
3333

3434
def __init__(self, node):
3535
self.network: Optional[Network] = None
36-
self.map = None # instance of Maps
36+
self.map = None # instance of PdoMaps
3737
self.node = node
3838

3939
def __iter__(self):
@@ -140,7 +140,7 @@ def stop(self):
140140
pdo_map.stop()
141141

142142

143-
class Maps(Mapping):
143+
class PdoMaps(Mapping[int, "PdoMap"]):
144144
"""A collection of transmit or receive maps."""
145145

146146
def __init__(self, com_offset, map_offset, pdo_node: PdoBase, cob_base=None):
@@ -150,10 +150,10 @@ def __init__(self, com_offset, map_offset, pdo_node: PdoBase, cob_base=None):
150150
:param pdo_node:
151151
:param cob_base:
152152
"""
153-
self.maps: Dict[int, "Map"] = {}
153+
self.maps: Dict[int, "PdoMap"] = {}
154154
for map_no in range(512):
155155
if com_offset + map_no in pdo_node.node.object_dictionary:
156-
new_map = Map(
156+
new_map = PdoMap(
157157
pdo_node,
158158
pdo_node.node.sdo[com_offset + map_no],
159159
pdo_node.node.sdo[map_offset + map_no])
@@ -162,7 +162,7 @@ def __init__(self, com_offset, map_offset, pdo_node: PdoBase, cob_base=None):
162162
new_map.predefined_cob_id = cob_base + map_no * 0x100 + pdo_node.node.id
163163
self.maps[map_no + 1] = new_map
164164

165-
def __getitem__(self, key: int) -> "Map":
165+
def __getitem__(self, key: int) -> "PdoMap":
166166
return self.maps[key]
167167

168168
def __iter__(self) -> Iterable[int]:
@@ -172,7 +172,7 @@ def __len__(self) -> int:
172172
return len(self.maps)
173173

174174

175-
class Map:
175+
class PdoMap:
176176
"""One message which can have up to 8 bytes of variables mapped."""
177177

178178
def __init__(self, pdo_node: PdoBase, com_record, map_array):
@@ -211,6 +211,9 @@ def __init__(self, pdo_node: PdoBase, com_record, map_array):
211211
self.is_received: bool = False
212212
self._task = None
213213

214+
def __repr__(self) -> str:
215+
return f"<{type(self).__qualname__} {self.name!r} at COB-ID 0x{self.cob_id}>"
216+
214217
def __getitem_by_index(self, value):
215218
valid_values = []
216219
for var in self.map:
@@ -340,12 +343,12 @@ async def aon_message(self, can_id, data, timestamp):
340343
if res is not None and asyncio.iscoroutine(res):
341344
await res
342345

343-
def add_callback(self, callback: Callable[["Map"], None]) -> None:
346+
def add_callback(self, callback: Callable[["PdoMap"], None]) -> None:
344347
"""Add a callback which will be called on receive.
345348
346349
:param callback:
347350
The function to call which must take one argument of a
348-
:class:`~canopen.pdo.Map`.
351+
:class:`~canopen.pdo.PdoMap`.
349352
"""
350353
self.callbacks.append(callback)
351354

@@ -734,3 +737,5 @@ async def aset_data(self, data: bytes):
734737

735738
# For compatibility
736739
Variable = PdoVariable
740+
Maps = PdoMaps
741+
Map = PdoMap

canopen/profiles/p402.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ class BaseNode402(RemoteNode):
214214
def __init__(self, node_id, object_dictionary):
215215
super(BaseNode402, self).__init__(node_id, object_dictionary)
216216
self.tpdo_values = {} # { index: value from last received TPDO }
217-
self.tpdo_pointers = {} # { index: pdo.Map instance }
218-
self.rpdo_pointers = {} # { index: pdo.Map instance }
217+
self.tpdo_pointers = {} # { index: pdo.PdoMap instance }
218+
self.rpdo_pointers = {} # { index: pdo.PdoMap instance }
219219

220220
def setup_402_state_machine(self, read_pdos=True):
221221
"""Configure the state machine by searching for a TPDO that has the StatusWord mapped.
@@ -474,7 +474,7 @@ def on_TPDOs_update_callback(self, mapobject):
474474
"""Cache updated values from a TPDO received from this node.
475475
476476
:param mapobject: The received PDO message.
477-
:type mapobject: canopen.pdo.Map
477+
:type mapobject: canopen.pdo.PdoMap
478478
"""
479479
# NOTE: Callback. Called from another thread unless async
480480
for obj in mapobject:

canopen/sdo/base.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ def __init__(self, sdo_node: SdoBase, od: ObjectDictionary):
114114
self.sdo_node = sdo_node
115115
self.od = od
116116

117+
def __repr__(self) -> str:
118+
return f"<{type(self).__qualname__} {self.od.name!r} at 0x{self.od.index:04x}>"
119+
117120
def __getitem__(self, subindex: Union[int, str]) -> "SdoVariable":
118121
return SdoVariable(self.sdo_node, self.od[subindex])
119122

@@ -130,6 +133,9 @@ def __aiter__(self):
130133
def __len__(self) -> int:
131134
return len(self.od)
132135

136+
async def alen(self) -> int:
137+
return len(self.od)
138+
133139
def __contains__(self, subindex: Union[int, str]) -> bool:
134140
return subindex in self.od
135141

@@ -140,14 +146,17 @@ def __init__(self, sdo_node: SdoBase, od: ObjectDictionary):
140146
self.sdo_node = sdo_node
141147
self.od = od
142148

149+
def __repr__(self) -> str:
150+
return f"<{type(self).__qualname__} {self.od.name!r} at 0x{self.od.index:04x}>"
151+
143152
def __getitem__(self, subindex: Union[int, str]) -> "SdoVariable":
144153
return SdoVariable(self.sdo_node, self.od[subindex])
145154

146155
def __iter__(self) -> Iterable[int]:
147-
return iter(range(1, len(self) + 1))
156+
return iter(self.od)
148157

149158
async def aiter(self):
150-
for i in range(1, await self[0].aget_raw() + 1):
159+
for i in iter(self.od):
151160
yield i
152161

153162
def __aiter__(self):
@@ -157,6 +166,9 @@ def __len__(self) -> int:
157166
# NOTE: Blocking - OK. Protected in SdoClient
158167
return self[0].get_raw()
159168

169+
async def alen(self) -> int:
170+
return await self[0].aget_raw()
171+
160172
def __contains__(self, subindex: int) -> bool:
161173
return 0 <= subindex <= len(self)
162174

canopen/sdo/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def __str__(self):
5454
text = text + ", " + self.CODES[self.code]
5555
return text
5656

57+
def __eq__(self, other):
58+
"""Compare two exception objects based on SDO abort code."""
59+
return self.code == other.code
60+
5761

5862
class SdoCommunicationError(SdoError):
5963
"""No or unexpected response from slave."""

canopen/variable.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ def __init__(self, od: objectdictionary.ODVariable):
2525
#: Holds a local, overridable copy of the Object Subindex
2626
self.subindex = od.subindex
2727

28+
def __repr__(self) -> str:
29+
suffix = f":{self.subindex:02x}" if isinstance(self.od.parent,
30+
(objectdictionary.ODRecord, objectdictionary.ODArray)
31+
) else ""
32+
return f"<{type(self).__qualname__} {self.name!r} at 0x{self.index:04x}{suffix}>"
33+
2834
def get_data(self) -> bytes:
2935
"""Byte representation of the object as :class:`bytes`."""
3036
raise NotImplementedError("Variable is not readable")

doc/pdo.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ API
8989

9090
.. describe:: pdo[no]
9191

92-
Return the :class:`canopen.pdo.Map` for the specified map number.
92+
Return the :class:`canopen.pdo.PdoMap` for the specified map number.
9393
First map starts at 1.
9494

9595
.. describe:: iter(pdo)
@@ -101,7 +101,7 @@ API
101101
Return the number of supported maps.
102102

103103

104-
.. autoclass:: canopen.pdo.Map
104+
.. autoclass:: canopen.pdo.PdoMap
105105
:members:
106106

107107
.. describe:: map[name]

0 commit comments

Comments
 (0)