6
6
import logging
7
7
import threading
8
8
from typing import Callable , Dict , Iterable , List , Optional , Union , TYPE_CHECKING
9
+ import asyncio
9
10
10
11
if TYPE_CHECKING :
11
12
from can import BusABC , Notifier
13
+ from asyncio import AbstractEventLoop
12
14
13
15
try :
14
16
import can
36
38
class Network (MutableMapping ):
37
39
"""Representation of one CAN bus containing one or more nodes."""
38
40
39
- def __init__ (self , bus = None ):
41
+ def __init__ (
42
+ self ,
43
+ bus : Optional [BusABC ] = None ,
44
+ loop : Optional [AbstractEventLoop ] = None
45
+ ):
40
46
"""
41
47
:param can.BusABC bus:
42
48
A python-can bus instance to re-use.
43
49
"""
44
50
#: A python-can :class:`can.BusABC` instance which is set after
45
51
#: :meth:`canopen.Network.connect` is called
46
52
self .bus : Optional [BusABC ] = bus
53
+ self .loop : Optional [asyncio .AbstractEventLoop ] = loop
47
54
#: A :class:`~canopen.network.NodeScanner` for detecting nodes
48
55
self .scanner = NodeScanner (self )
49
56
#: List of :class:`can.Listener` objects.
@@ -52,15 +59,19 @@ def __init__(self, bus=None):
52
59
self .notifier : Optional [Notifier ] = None
53
60
self .nodes : Dict [int , Union [RemoteNode , LocalNode ]] = {}
54
61
self .subscribers : Dict [int , List [Callback ]] = {}
55
- self .send_lock = threading .Lock ()
62
+ self .send_lock = threading .Lock () # FIXME Async
56
63
self .sync = SyncProducer (self )
57
64
self .time = TimeProducer (self )
58
65
self .nmt = NmtMaster (0 )
59
66
self .nmt .network = self
60
67
61
68
self .lss = LssMaster ()
62
69
self .lss .network = self
63
- self .subscribe (self .lss .LSS_RX_COBID , self .lss .on_message_received )
70
+
71
+ if self .loop :
72
+ self .subscribe (self .lss .LSS_RX_COBID , self .lss .aon_message_received )
73
+ else :
74
+ self .subscribe (self .lss .LSS_RX_COBID , self .lss .on_message_received )
64
75
65
76
def subscribe (self , can_id : int , callback : Callback ) -> None :
66
77
"""Listen for messages with a specific CAN ID.
@@ -119,8 +130,9 @@ def connect(self, *args, **kwargs) -> "Network":
119
130
kwargs_notifier = {}
120
131
if "loop" in kwargs :
121
132
kwargs_notifier ["loop" ] = kwargs ["loop" ]
133
+ self .loop = kwargs ["loop" ]
122
134
del kwargs ["loop" ]
123
- self .bus = can .interface . Bus (* args , ** kwargs )
135
+ self .bus = can .Bus (* args , ** kwargs )
124
136
logger .info ("Connected to '%s'" , self .bus .channel_info )
125
137
self .notifier = can .Notifier (self .bus , self .listeners , 1 , ** kwargs_notifier )
126
138
return self
@@ -220,7 +232,9 @@ def send_message(self, can_id: int, data: bytes, remote: bool = False) -> None:
220
232
arbitration_id = can_id ,
221
233
data = data ,
222
234
is_remote_frame = remote )
223
- with self .send_lock :
235
+ # NOTE: This lock is ok for async, because ther is only one thread
236
+ # calling this function when using async, so it'll never lock.
237
+ with self .send_lock : # FIXME: Blocking
224
238
self .bus .send (msg )
225
239
self .check ()
226
240
@@ -256,10 +270,13 @@ def notify(self, can_id: int, data: bytearray, timestamp: float) -> None:
256
270
:param timestamp:
257
271
Timestamp of the message, preferably as a Unix timestamp
258
272
"""
259
- if can_id in self .subscribers :
260
- callbacks = self .subscribers [can_id ]
273
+ # NOTE: Callback. Will be called from another thread
274
+ callbacks = self .subscribers .get (can_id )
275
+ if callbacks is not None :
261
276
for callback in callbacks :
262
- callback (can_id , data , timestamp )
277
+ res = callback (can_id , data , timestamp )
278
+ if res is not None and self .loop is not None and asyncio .iscoroutine (res ):
279
+ self .loop .create_task (res )
263
280
self .scanner .on_message_received (can_id )
264
281
265
282
def check (self ) -> None :
@@ -360,6 +377,7 @@ def __init__(self, network: Network):
360
377
self .network = network
361
378
362
379
def on_message_received (self , msg ):
380
+ # NOTE: Callback. Will be called from another thread
363
381
if msg .is_error_frame or msg .is_remote_frame :
364
382
return
365
383
@@ -394,9 +412,11 @@ def __init__(self, network: Optional[Network] = None):
394
412
self .nodes : List [int ] = []
395
413
396
414
def on_message_received (self , can_id : int ):
415
+ # NOTE: Callback. Will be called from another thread
397
416
service = can_id & 0x780
398
417
node_id = can_id & 0x7F
399
418
if node_id not in self .nodes and node_id != 0 and service in self .SERVICES :
419
+ # NOTE: Assume this is thread-safe
400
420
self .nodes .append (node_id )
401
421
402
422
def reset (self ):
0 commit comments