Skip to content

Commit cacc419

Browse files
Add type annotations
1 parent ff8b5ca commit cacc419

File tree

16 files changed

+342
-252
lines changed

16 files changed

+342
-252
lines changed

canopen/emcy.py

+14-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import threading
44
import time
5+
from typing import Callable, List, Optional
56

67
# Error code, error register, vendor specific data
78
EMCY_STRUCT = struct.Struct("<HB5s")
@@ -13,9 +14,9 @@ class EmcyConsumer(object):
1314

1415
def __init__(self):
1516
#: Log of all received EMCYs for this node
16-
self.log = []
17+
self.log: List["EmcyError"] = []
1718
#: Only active EMCYs. Will be cleared on Error Reset
18-
self.active = []
19+
self.active: List["EmcyError"] = []
1920
self.callbacks = []
2021
self.emcy_received = threading.Condition()
2122

@@ -35,7 +36,7 @@ def on_emcy(self, can_id, data, timestamp):
3536
for callback in self.callbacks:
3637
callback(entry)
3738

38-
def add_callback(self, callback):
39+
def add_callback(self, callback: Callable[["EmcyError"], None]):
3940
"""Get notified on EMCY messages from this node.
4041
4142
:param callback:
@@ -49,14 +50,15 @@ def reset(self):
4950
self.log = []
5051
self.active = []
5152

52-
def wait(self, emcy_code=None, timeout=10):
53+
def wait(
54+
self, emcy_code: Optional[int] = None, timeout: float = 10
55+
) -> "EmcyError":
5356
"""Wait for a new EMCY to arrive.
5457
55-
:param int emcy_code: EMCY code to wait for
56-
:param float timeout: Max time in seconds to wait
58+
:param emcy_code: EMCY code to wait for
59+
:param timeout: Max time in seconds to wait
5760
5861
:return: The EMCY exception object or None if timeout
59-
:rtype: canopen.emcy.EmcyError
6062
"""
6163
end_time = time.time() + timeout
6264
while True:
@@ -79,15 +81,15 @@ def wait(self, emcy_code=None, timeout=10):
7981

8082
class EmcyProducer(object):
8183

82-
def __init__(self, cob_id):
84+
def __init__(self, cob_id: int):
8385
self.network = None
8486
self.cob_id = cob_id
8587

86-
def send(self, code, register=0, data=b""):
88+
def send(self, code: int, register: int = 0, data: bytes = b""):
8789
payload = EMCY_STRUCT.pack(code, register, data)
8890
self.network.send_message(self.cob_id, payload)
8991

90-
def reset(self, register=0, data=b""):
92+
def reset(self, register: int = 0, data: bytes = b""):
9193
payload = EMCY_STRUCT.pack(0, register, data)
9294
self.network.send_message(self.cob_id, payload)
9395

@@ -111,7 +113,7 @@ class EmcyError(Exception):
111113
(0xFF00, 0xFF00, "Device Specific")
112114
]
113115

114-
def __init__(self, code, register, data, timestamp):
116+
def __init__(self, code: int, register: int, data: bytes, timestamp: float):
115117
#: EMCY code
116118
self.code = code
117119
#: Error register
@@ -121,7 +123,7 @@ def __init__(self, code, register, data, timestamp):
121123
#: Timestamp of message
122124
self.timestamp = timestamp
123125

124-
def get_desc(self):
126+
def get_desc(self) -> str:
125127
for code, mask, description in self.DESCRIPTIONS:
126128
if self.code & mask == code:
127129
return description

canopen/network.py

+58-41
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from collections import MutableMapping
55
import logging
66
import threading
7-
import struct
7+
from typing import Callable, Dict, Iterable, List, Optional, Union
88

99
try:
1010
import can
@@ -22,9 +22,12 @@
2222
from .nmt import NmtMaster
2323
from .lss import LssMaster
2424
from .objectdictionary.eds import import_from_node
25+
from .objectdictionary import ObjectDictionary
2526

2627
logger = logging.getLogger(__name__)
2728

29+
Callback = Callable[[int, bytearray, float], None]
30+
2831

2932
class Network(MutableMapping):
3033
"""Representation of one CAN bus containing one or more nodes."""
@@ -43,8 +46,8 @@ def __init__(self, bus=None):
4346
#: Includes at least MessageListener.
4447
self.listeners = [MessageListener(self)]
4548
self.notifier = None
46-
self.nodes = {}
47-
self.subscribers = {}
49+
self.nodes: Dict[int, Union[RemoteNode, LocalNode]] = {}
50+
self.subscribers: Dict[int, List[Callback]] = {}
4851
self.send_lock = threading.Lock()
4952
self.sync = SyncProducer(self)
5053
self.time = TimeProducer(self)
@@ -55,10 +58,10 @@ def __init__(self, bus=None):
5558
self.lss.network = self
5659
self.subscribe(self.lss.LSS_RX_COBID, self.lss.on_message_received)
5760

58-
def subscribe(self, can_id, callback):
61+
def subscribe(self, can_id: int, callback: Callback) -> None:
5962
"""Listen for messages with a specific CAN ID.
6063
61-
:param int can_id:
64+
:param can_id:
6265
The CAN ID to listen for.
6366
:param callback:
6467
Function to call when message is received.
@@ -67,7 +70,7 @@ def subscribe(self, can_id, callback):
6770
if callback not in self.subscribers[can_id]:
6871
self.subscribers[can_id].append(callback)
6972

70-
def unsubscribe(self, can_id, callback=None):
73+
def unsubscribe(self, can_id, callback=None) -> None:
7174
"""Stop listening for message.
7275
7376
:param int can_id:
@@ -81,7 +84,7 @@ def unsubscribe(self, can_id, callback=None):
8184
else:
8285
self.subscribers[can_id].remove(callback)
8386

84-
def connect(self, *args, **kwargs):
87+
def connect(self, *args, **kwargs) -> "Network":
8588
"""Connect to CAN bus using python-can.
8689
8790
Arguments are passed directly to :class:`can.BusABC`. Typically these
@@ -111,7 +114,7 @@ def connect(self, *args, **kwargs):
111114
self.notifier = can.Notifier(self.bus, self.listeners, 1)
112115
return self
113116

114-
def disconnect(self):
117+
def disconnect(self) -> None:
115118
"""Disconnect from the CAN bus.
116119
117120
Must be overridden in a subclass if a custom interface is used.
@@ -132,7 +135,12 @@ def __enter__(self):
132135
def __exit__(self, type, value, traceback):
133136
self.disconnect()
134137

135-
def add_node(self, node, object_dictionary=None, upload_eds=False):
138+
def add_node(
139+
self,
140+
node: Union[int, RemoteNode, LocalNode],
141+
object_dictionary: Union[str, ObjectDictionary, None] = None,
142+
upload_eds: bool = False,
143+
) -> RemoteNode:
136144
"""Add a remote node to the network.
137145
138146
:param node:
@@ -142,12 +150,11 @@ def add_node(self, node, object_dictionary=None, upload_eds=False):
142150
Can be either a string for specifying the path to an
143151
Object Dictionary file or a
144152
:class:`canopen.ObjectDictionary` object.
145-
:param bool upload_eds:
153+
:param upload_eds:
146154
Set ``True`` if EDS file should be uploaded from 0x1021.
147155
148156
:return:
149157
The Node object that was added.
150-
:rtype: canopen.RemoteNode
151158
"""
152159
if isinstance(node, int):
153160
if upload_eds:
@@ -157,7 +164,11 @@ def add_node(self, node, object_dictionary=None, upload_eds=False):
157164
self[node.id] = node
158165
return node
159166

160-
def create_node(self, node, object_dictionary=None):
167+
def create_node(
168+
self,
169+
node: int,
170+
object_dictionary: Union[str, ObjectDictionary, None] = None,
171+
) -> LocalNode:
161172
"""Create a local node in the network.
162173
163174
:param node:
@@ -169,14 +180,13 @@ def create_node(self, node, object_dictionary=None):
169180
170181
:return:
171182
The Node object that was added.
172-
:rtype: canopen.LocalNode
173183
"""
174184
if isinstance(node, int):
175185
node = LocalNode(node, object_dictionary)
176186
self[node.id] = node
177187
return node
178188

179-
def send_message(self, can_id, data, remote=False):
189+
def send_message(self, can_id: int, data: bytes, remote: bool = False) -> None:
180190
"""Send a raw CAN message to the network.
181191
182192
This method may be overridden in a subclass if you need to integrate
@@ -203,35 +213,36 @@ def send_message(self, can_id, data, remote=False):
203213
self.bus.send(msg)
204214
self.check()
205215

206-
def send_periodic(self, can_id, data, period, remote=False):
216+
def send_periodic(
217+
self, can_id: int, data: bytes, period: float, remote: bool = False
218+
) -> "PeriodicMessageTask":
207219
"""Start sending a message periodically.
208220
209-
:param int can_id:
221+
:param can_id:
210222
CAN-ID of the message
211223
:param data:
212224
Data to be transmitted (anything that can be converted to bytes)
213-
:param float period:
225+
:param period:
214226
Seconds between each message
215-
:param bool remote:
227+
:param remote:
216228
indicates if the message frame is a remote request to the slave node
217229
218230
:return:
219231
An task object with a ``.stop()`` method to stop the transmission
220-
:rtype: canopen.network.PeriodicMessageTask
221232
"""
222233
return PeriodicMessageTask(can_id, data, period, self.bus, remote)
223234

224-
def notify(self, can_id, data, timestamp):
235+
def notify(self, can_id: int, data: bytearray, timestamp: float) -> None:
225236
"""Feed incoming message to this library.
226237
227238
If a custom interface is used, this function must be called for each
228239
message read from the CAN bus.
229240
230-
:param int can_id:
241+
:param can_id:
231242
CAN-ID of the message
232-
:param bytearray data:
243+
:param data:
233244
Data part of the message (0 - 8 bytes)
234-
:param float timestamp:
245+
:param timestamp:
235246
Timestamp of the message, preferably as a Unix timestamp
236247
"""
237248
if can_id in self.subscribers:
@@ -240,7 +251,7 @@ def notify(self, can_id, data, timestamp):
240251
callback(can_id, data, timestamp)
241252
self.scanner.on_message_received(can_id)
242253

243-
def check(self):
254+
def check(self) -> None:
244255
"""Check that no fatal error has occurred in the receiving thread.
245256
246257
If an exception caused the thread to terminate, that exception will be
@@ -252,22 +263,22 @@ def check(self):
252263
logger.error("An error has caused receiving of messages to stop")
253264
raise exc
254265

255-
def __getitem__(self, node_id):
266+
def __getitem__(self, node_id: int) -> Union[RemoteNode, LocalNode]:
256267
return self.nodes[node_id]
257268

258-
def __setitem__(self, node_id, node):
269+
def __setitem__(self, node_id: int, node: Union[RemoteNode, LocalNode]):
259270
assert node_id == node.id
260271
self.nodes[node_id] = node
261272
node.associate_network(self)
262273

263-
def __delitem__(self, node_id):
274+
def __delitem__(self, node_id: int):
264275
self.nodes[node_id].remove_network()
265276
del self.nodes[node_id]
266277

267-
def __iter__(self):
278+
def __iter__(self) -> Iterable[int]:
268279
return iter(self.nodes)
269280

270-
def __len__(self):
281+
def __len__(self) -> int:
271282
return len(self.nodes)
272283

273284

@@ -277,13 +288,20 @@ class PeriodicMessageTask(object):
277288
CyclicSendTask
278289
"""
279290

280-
def __init__(self, can_id, data, period, bus, remote=False):
281-
"""
282-
:param int can_id:
291+
def __init__(
292+
self,
293+
can_id: int,
294+
data: bytes,
295+
period: float,
296+
bus,
297+
remote: bool = False,
298+
):
299+
"""
300+
:param can_id:
283301
CAN-ID of the message
284302
:param data:
285303
Data to be transmitted (anything that can be converted to bytes)
286-
:param float period:
304+
:param period:
287305
Seconds between each message
288306
:param can.BusABC bus:
289307
python-can bus to use for transmission
@@ -303,7 +321,7 @@ def stop(self):
303321
"""Stop transmission"""
304322
self._task.stop()
305323

306-
def update(self, data):
324+
def update(self, data: bytes) -> None:
307325
"""Update data of message
308326
309327
:param data:
@@ -323,11 +341,11 @@ def update(self, data):
323341
class MessageListener(Listener):
324342
"""Listens for messages on CAN bus and feeds them to a Network instance.
325343
326-
:param canopen.Network network:
344+
:param network:
327345
The network to notify on new messages.
328346
"""
329347

330-
def __init__(self, network):
348+
def __init__(self, network: Network):
331349
self.network = network
332350

333351
def on_message_received(self, msg):
@@ -359,12 +377,12 @@ class NodeScanner(object):
359377

360378
SERVICES = (0x700, 0x580, 0x180, 0x280, 0x380, 0x480, 0x80)
361379

362-
def __init__(self, network=None):
380+
def __init__(self, network: Optional[Network] = None):
363381
self.network = network
364382
#: A :class:`list` of nodes discovered
365-
self.nodes = []
383+
self.nodes: List[int] = []
366384

367-
def on_message_received(self, can_id):
385+
def on_message_received(self, can_id: int):
368386
service = can_id & 0x780
369387
node_id = can_id & 0x7F
370388
if node_id not in self.nodes and node_id != 0 and service in self.SERVICES:
@@ -374,11 +392,10 @@ def reset(self):
374392
"""Clear list of found nodes."""
375393
self.nodes = []
376394

377-
def search(self, limit=127):
395+
def search(self, limit: int = 127) -> None:
378396
"""Search for nodes by sending SDO requests to all node IDs."""
379397
if self.network is None:
380398
raise RuntimeError("A Network is required to do active scanning")
381399
sdo_req = b"\x40\x00\x10\x00\x00\x00\x00\x00"
382400
for node_id in range(1, limit + 1):
383401
self.network.send_message(0x600 + node_id, sdo_req)
384-

0 commit comments

Comments
 (0)