9
9
10
10
from __future__ import absolute_import
11
11
12
- import can
12
+ import sys
13
13
import importlib
14
-
15
- from can .broadcastmanager import CyclicSendTaskABC , MultiRateCyclicSendTaskABC
16
14
from pkg_resources import iter_entry_points
17
- from can .util import load_config
15
+ import logging
16
+
17
+ import can
18
+ from .bus import BusABC
19
+ from .broadcastmanager import CyclicSendTaskABC , MultiRateCyclicSendTaskABC
20
+ from .util import load_config
21
+
22
+ if sys .version_info .major > 2 :
23
+ basestring = str
24
+
25
+
26
+ log = logging .getLogger ('can.interface' )
27
+ log_autodetect = log .getChild ('detect_available_configs' )
18
28
19
29
# interface_name => (module, classname)
20
30
BACKENDS = {
21
- 'kvaser' : ('can.interfaces.kvaser' , 'KvaserBus' ),
22
- 'socketcan_ctypes' : ('can.interfaces.socketcan' , 'SocketcanCtypes_Bus' ),
23
- 'socketcan_native' : ('can.interfaces.socketcan' , 'SocketcanNative_Bus' ),
24
- 'serial' : ('can.interfaces.serial.serial_can' , 'SerialBus' ),
25
- 'pcan' : ('can.interfaces.pcan' , 'PcanBus' ),
26
- 'usb2can' : ('can.interfaces.usb2can' , 'Usb2canBus' ),
27
- 'ixxat' : ('can.interfaces.ixxat' , 'IXXATBus' ),
28
- 'nican' : ('can.interfaces.nican' , 'NicanBus' ),
29
- 'iscan' : ('can.interfaces.iscan' , 'IscanBus' ),
30
- 'virtual' : ('can.interfaces.virtual' , 'VirtualBus' ),
31
- 'neovi' : ('can.interfaces.ics_neovi' , 'NeoViBus' ),
32
- 'vector' : ('can.interfaces.vector' , 'VectorBus' ),
33
- 'slcan' : ('can.interfaces.slcan' , 'slcanBus' )
31
+ 'kvaser' : ('can.interfaces.kvaser' , 'KvaserBus' ),
32
+ 'socketcan_ctypes' : ('can.interfaces.socketcan' , 'SocketcanCtypes_Bus' ),
33
+ 'socketcan_native' : ('can.interfaces.socketcan' , 'SocketcanNative_Bus' ),
34
+ 'serial' : ('can.interfaces.serial.serial_can' ,'SerialBus' ),
35
+ 'pcan' : ('can.interfaces.pcan' , 'PcanBus' ),
36
+ 'usb2can' : ('can.interfaces.usb2can' , 'Usb2canBus' ),
37
+ 'ixxat' : ('can.interfaces.ixxat' , 'IXXATBus' ),
38
+ 'nican' : ('can.interfaces.nican' , 'NicanBus' ),
39
+ 'iscan' : ('can.interfaces.iscan' , 'IscanBus' ),
40
+ 'virtual' : ('can.interfaces.virtual' , 'VirtualBus' ),
41
+ 'neovi' : ('can.interfaces.ics_neovi' , 'NeoViBus' ),
42
+ 'vector' : ('can.interfaces.vector' , 'VectorBus' ),
43
+ 'slcan' : ('can.interfaces.slcan' , 'slcanBus' )
34
44
}
35
45
36
-
37
46
BACKENDS .update ({
38
47
interface .name : (interface .module_name , interface .attrs [0 ])
39
48
for interface in iter_entry_points ('python_can.interface' )
40
49
})
41
50
42
51
43
- class Bus (object ):
52
+ def _get_class_for_interface (interface ):
53
+ """
54
+ Returns the main bus class for the given interface.
55
+
56
+ :raises:
57
+ NotImplementedError if the interface is not known
58
+ :raises:
59
+ ImportError if there was a problem while importing the
60
+ interface or the bus class within that
61
+ """
62
+
63
+ # filter out the socketcan special case
64
+ if interface == 'socketcan' :
65
+ try :
66
+ interface = can .util .choose_socketcan_implementation ()
67
+ except Exception as e :
68
+ raise ImportError ("Cannot choose socketcan implementation: {}" .format (e ))
69
+
70
+ # Find the correct backend
71
+ try :
72
+ module_name , class_name = BACKENDS [interface ]
73
+ except KeyError :
74
+ raise NotImplementedError ("CAN interface '{}' not supported" .format (interface ))
75
+
76
+ # Import the correct interface module
77
+ try :
78
+ module = importlib .import_module (module_name )
79
+ except Exception as e :
80
+ raise ImportError (
81
+ "Cannot import module {} for CAN interface '{}': {}" .format (module_name , interface , e )
82
+ )
83
+
84
+ # Get the correct class
85
+ try :
86
+ bus_class = getattr (module , class_name )
87
+ except Exception as e :
88
+ raise ImportError (
89
+ "Cannot import class {} from module {} for CAN interface '{}': {}"
90
+ .format (class_name , module_name , interface , e )
91
+ )
92
+
93
+ return bus_class
94
+
95
+
96
+ class Bus (BusABC ):
44
97
"""
45
98
Instantiates a CAN Bus of the given `bustype`, falls back to reading a
46
99
configuration file from default locations.
@@ -61,39 +114,77 @@ def __new__(cls, other, channel=None, *args, **kwargs):
61
114
or set in the can.rc config.
62
115
63
116
"""
117
+
118
+ # Figure out the configuration
64
119
config = load_config (config = {
65
- 'interface' : kwargs .get ('bustype' ),
120
+ 'interface' : kwargs .get ('bustype' , kwargs . get ( 'interface' ) ),
66
121
'channel' : channel
67
122
})
68
123
124
+ # remove the bustype & interface so it doesn't get passed to the backend
69
125
if 'bustype' in kwargs :
70
- # remove the bustype so it doesn't get passed to the backend
71
126
del kwargs ['bustype' ]
72
- interface = config [ 'interface' ]
73
- channel = config [ 'channel ' ]
127
+ if 'interface' in kwargs :
128
+ del kwargs [ 'interface ' ]
74
129
75
- # Import the correct Bus backend
76
- try :
77
- (module_name , class_name ) = BACKENDS [interface ]
78
- except KeyError :
79
- raise NotImplementedError ("CAN interface '{}' not supported" .format (interface ))
130
+ cls = _get_class_for_interface (config ['interface' ])
131
+ return cls (channel = config ['channel' ], * args , ** kwargs )
132
+
133
+
134
+ def detect_available_configs (interfaces = None ):
135
+ """Detect all configurations/channels that the interfaces could
136
+ currently connect with.
137
+
138
+ This might be quite time consuming.
139
+
140
+ Automated configuration detection may not be implemented by
141
+ every interface on every platform. This method will not raise
142
+ an error in that case, but with rather return an empty list
143
+ for that interface.
144
+
145
+ :param interfaces: either
146
+ - the name of an interface to be searched in as a string,
147
+ - an iterable of interface names to search in, or
148
+ - `None` to search in all known interfaces.
149
+ :rtype: list of `dict`s
150
+ :return: an iterable of dicts, each suitable for usage in
151
+ :class:`can.interface.Bus`'s constructor.
152
+ """
153
+
154
+ # Figure out where to search
155
+ if interfaces is None :
156
+ # use an iterator over the keys so we do not have to copy it
157
+ interfaces = BACKENDS .keys ()
158
+ elif isinstance (interfaces , basestring ):
159
+ interfaces = [interfaces , ]
160
+ # else it is supposed to be an iterable of strings
161
+
162
+ result = []
163
+ for interface in interfaces :
80
164
81
165
try :
82
- module = importlib .import_module (module_name )
83
- except Exception as e :
84
- raise ImportError (
85
- "Cannot import module {} for CAN interface '{}': {}" .format (module_name , interface , e )
86
- )
166
+ bus_class = _get_class_for_interface (interface )
167
+ except ImportError :
168
+ log_autodetect .debug ('interface "%s" can not be loaded for detection of available configurations' , interface )
169
+ continue
170
+
171
+ # get available channels
87
172
try :
88
- cls = getattr (module , class_name )
89
- except Exception as e :
90
- raise ImportError (
91
- "Cannot import class {} from module {} for CAN interface '{}': {}" .format (
92
- class_name , module_name , interface , e
93
- )
94
- )
173
+ available = list (bus_class ._detect_available_configs ())
174
+ except NotImplementedError :
175
+ log_autodetect .debug ('interface "%s" does not support detection of available configurations' , interface )
176
+ else :
177
+ log_autodetect .debug ('interface "%s" detected %i available configurations' , interface , len (available ))
178
+
179
+ # add the interface name to the configs if it is not already present
180
+ for config in available :
181
+ if 'interface' not in config :
182
+ config ['interface' ] = interface
183
+
184
+ # append to result
185
+ result += available
95
186
96
- return cls ( channel , ** kwargs )
187
+ return result
97
188
98
189
99
190
class CyclicSendTask (CyclicSendTaskABC ):
0 commit comments