4
4
from collections import MutableMapping
5
5
import logging
6
6
import threading
7
- import struct
7
+ from typing import Callable , Dict , Iterable , List , Optional , Union
8
8
9
9
try :
10
10
import can
22
22
from .nmt import NmtMaster
23
23
from .lss import LssMaster
24
24
from .objectdictionary .eds import import_from_node
25
+ from .objectdictionary import ObjectDictionary
25
26
26
27
logger = logging .getLogger (__name__ )
27
28
29
+ Callback = Callable [[int , bytearray , float ], None ]
30
+
28
31
29
32
class Network (MutableMapping ):
30
33
"""Representation of one CAN bus containing one or more nodes."""
@@ -43,8 +46,8 @@ def __init__(self, bus=None):
43
46
#: Includes at least MessageListener.
44
47
self .listeners = [MessageListener (self )]
45
48
self .notifier = None
46
- self .nodes = {}
47
- self .subscribers = {}
49
+ self .nodes : Dict [ int , Union [ RemoteNode , LocalNode ]] = {}
50
+ self .subscribers : Dict [ int , List [ Callback ]] = {}
48
51
self .send_lock = threading .Lock ()
49
52
self .sync = SyncProducer (self )
50
53
self .time = TimeProducer (self )
@@ -55,10 +58,10 @@ def __init__(self, bus=None):
55
58
self .lss .network = self
56
59
self .subscribe (self .lss .LSS_RX_COBID , self .lss .on_message_received )
57
60
58
- def subscribe (self , can_id , callback ) :
61
+ def subscribe (self , can_id : int , callback : Callback ) -> None :
59
62
"""Listen for messages with a specific CAN ID.
60
63
61
- :param int can_id:
64
+ :param can_id:
62
65
The CAN ID to listen for.
63
66
:param callback:
64
67
Function to call when message is received.
@@ -67,7 +70,7 @@ def subscribe(self, can_id, callback):
67
70
if callback not in self .subscribers [can_id ]:
68
71
self .subscribers [can_id ].append (callback )
69
72
70
- def unsubscribe (self , can_id , callback = None ):
73
+ def unsubscribe (self , can_id , callback = None ) -> None :
71
74
"""Stop listening for message.
72
75
73
76
:param int can_id:
@@ -81,7 +84,7 @@ def unsubscribe(self, can_id, callback=None):
81
84
else :
82
85
self .subscribers [can_id ].remove (callback )
83
86
84
- def connect (self , * args , ** kwargs ):
87
+ def connect (self , * args , ** kwargs ) -> "Network" :
85
88
"""Connect to CAN bus using python-can.
86
89
87
90
Arguments are passed directly to :class:`can.BusABC`. Typically these
@@ -111,7 +114,7 @@ def connect(self, *args, **kwargs):
111
114
self .notifier = can .Notifier (self .bus , self .listeners , 1 )
112
115
return self
113
116
114
- def disconnect (self ):
117
+ def disconnect (self ) -> None :
115
118
"""Disconnect from the CAN bus.
116
119
117
120
Must be overridden in a subclass if a custom interface is used.
@@ -132,7 +135,12 @@ def __enter__(self):
132
135
def __exit__ (self , type , value , traceback ):
133
136
self .disconnect ()
134
137
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 :
136
144
"""Add a remote node to the network.
137
145
138
146
:param node:
@@ -142,12 +150,11 @@ def add_node(self, node, object_dictionary=None, upload_eds=False):
142
150
Can be either a string for specifying the path to an
143
151
Object Dictionary file or a
144
152
:class:`canopen.ObjectDictionary` object.
145
- :param bool upload_eds:
153
+ :param upload_eds:
146
154
Set ``True`` if EDS file should be uploaded from 0x1021.
147
155
148
156
:return:
149
157
The Node object that was added.
150
- :rtype: canopen.RemoteNode
151
158
"""
152
159
if isinstance (node , int ):
153
160
if upload_eds :
@@ -157,7 +164,11 @@ def add_node(self, node, object_dictionary=None, upload_eds=False):
157
164
self [node .id ] = node
158
165
return node
159
166
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 :
161
172
"""Create a local node in the network.
162
173
163
174
:param node:
@@ -169,14 +180,13 @@ def create_node(self, node, object_dictionary=None):
169
180
170
181
:return:
171
182
The Node object that was added.
172
- :rtype: canopen.LocalNode
173
183
"""
174
184
if isinstance (node , int ):
175
185
node = LocalNode (node , object_dictionary )
176
186
self [node .id ] = node
177
187
return node
178
188
179
- def send_message (self , can_id , data , remote = False ):
189
+ def send_message (self , can_id : int , data : bytes , remote : bool = False ) -> None :
180
190
"""Send a raw CAN message to the network.
181
191
182
192
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):
203
213
self .bus .send (msg )
204
214
self .check ()
205
215
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" :
207
219
"""Start sending a message periodically.
208
220
209
- :param int can_id:
221
+ :param can_id:
210
222
CAN-ID of the message
211
223
:param data:
212
224
Data to be transmitted (anything that can be converted to bytes)
213
- :param float period:
225
+ :param period:
214
226
Seconds between each message
215
- :param bool remote:
227
+ :param remote:
216
228
indicates if the message frame is a remote request to the slave node
217
229
218
230
:return:
219
231
An task object with a ``.stop()`` method to stop the transmission
220
- :rtype: canopen.network.PeriodicMessageTask
221
232
"""
222
233
return PeriodicMessageTask (can_id , data , period , self .bus , remote )
223
234
224
- def notify (self , can_id , data , timestamp ) :
235
+ def notify (self , can_id : int , data : bytearray , timestamp : float ) -> None :
225
236
"""Feed incoming message to this library.
226
237
227
238
If a custom interface is used, this function must be called for each
228
239
message read from the CAN bus.
229
240
230
- :param int can_id:
241
+ :param can_id:
231
242
CAN-ID of the message
232
- :param bytearray data:
243
+ :param data:
233
244
Data part of the message (0 - 8 bytes)
234
- :param float timestamp:
245
+ :param timestamp:
235
246
Timestamp of the message, preferably as a Unix timestamp
236
247
"""
237
248
if can_id in self .subscribers :
@@ -240,7 +251,7 @@ def notify(self, can_id, data, timestamp):
240
251
callback (can_id , data , timestamp )
241
252
self .scanner .on_message_received (can_id )
242
253
243
- def check (self ):
254
+ def check (self ) -> None :
244
255
"""Check that no fatal error has occurred in the receiving thread.
245
256
246
257
If an exception caused the thread to terminate, that exception will be
@@ -252,22 +263,22 @@ def check(self):
252
263
logger .error ("An error has caused receiving of messages to stop" )
253
264
raise exc
254
265
255
- def __getitem__ (self , node_id ) :
266
+ def __getitem__ (self , node_id : int ) -> Union [ RemoteNode , LocalNode ] :
256
267
return self .nodes [node_id ]
257
268
258
- def __setitem__ (self , node_id , node ):
269
+ def __setitem__ (self , node_id : int , node : Union [ RemoteNode , LocalNode ] ):
259
270
assert node_id == node .id
260
271
self .nodes [node_id ] = node
261
272
node .associate_network (self )
262
273
263
- def __delitem__ (self , node_id ):
274
+ def __delitem__ (self , node_id : int ):
264
275
self .nodes [node_id ].remove_network ()
265
276
del self .nodes [node_id ]
266
277
267
- def __iter__ (self ):
278
+ def __iter__ (self ) -> Iterable [ int ] :
268
279
return iter (self .nodes )
269
280
270
- def __len__ (self ):
281
+ def __len__ (self ) -> int :
271
282
return len (self .nodes )
272
283
273
284
@@ -277,13 +288,20 @@ class PeriodicMessageTask(object):
277
288
CyclicSendTask
278
289
"""
279
290
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:
283
301
CAN-ID of the message
284
302
:param data:
285
303
Data to be transmitted (anything that can be converted to bytes)
286
- :param float period:
304
+ :param period:
287
305
Seconds between each message
288
306
:param can.BusABC bus:
289
307
python-can bus to use for transmission
@@ -303,7 +321,7 @@ def stop(self):
303
321
"""Stop transmission"""
304
322
self ._task .stop ()
305
323
306
- def update (self , data ) :
324
+ def update (self , data : bytes ) -> None :
307
325
"""Update data of message
308
326
309
327
:param data:
@@ -323,11 +341,11 @@ def update(self, data):
323
341
class MessageListener (Listener ):
324
342
"""Listens for messages on CAN bus and feeds them to a Network instance.
325
343
326
- :param canopen.Network network:
344
+ :param network:
327
345
The network to notify on new messages.
328
346
"""
329
347
330
- def __init__ (self , network ):
348
+ def __init__ (self , network : Network ):
331
349
self .network = network
332
350
333
351
def on_message_received (self , msg ):
@@ -359,12 +377,12 @@ class NodeScanner(object):
359
377
360
378
SERVICES = (0x700 , 0x580 , 0x180 , 0x280 , 0x380 , 0x480 , 0x80 )
361
379
362
- def __init__ (self , network = None ):
380
+ def __init__ (self , network : Optional [ Network ] = None ):
363
381
self .network = network
364
382
#: A :class:`list` of nodes discovered
365
- self .nodes = []
383
+ self .nodes : List [ int ] = []
366
384
367
- def on_message_received (self , can_id ):
385
+ def on_message_received (self , can_id : int ):
368
386
service = can_id & 0x780
369
387
node_id = can_id & 0x7F
370
388
if node_id not in self .nodes and node_id != 0 and service in self .SERVICES :
@@ -374,11 +392,10 @@ def reset(self):
374
392
"""Clear list of found nodes."""
375
393
self .nodes = []
376
394
377
- def search (self , limit = 127 ):
395
+ def search (self , limit : int = 127 ) -> None :
378
396
"""Search for nodes by sending SDO requests to all node IDs."""
379
397
if self .network is None :
380
398
raise RuntimeError ("A Network is required to do active scanning" )
381
399
sdo_req = b"\x40 \x00 \x10 \x00 \x00 \x00 \x00 \x00 "
382
400
for node_id in range (1 , limit + 1 ):
383
401
self .network .send_message (0x600 + node_id , sdo_req )
384
-
0 commit comments