99
1010from __future__ import absolute_import
1111
12- import can
12+ import sys
1313import importlib
14-
15- from can .broadcastmanager import CyclicSendTaskABC , MultiRateCyclicSendTaskABC
1614from 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' )
1828
1929# interface_name => (module, classname)
2030BACKENDS = {
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' )
3444}
3545
36-
3746BACKENDS .update ({
3847 interface .name : (interface .module_name , interface .attrs [0 ])
3948 for interface in iter_entry_points ('python_can.interface' )
4049})
4150
4251
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 ):
4497 """
4598 Instantiates a CAN Bus of the given `bustype`, falls back to reading a
4699 configuration file from default locations.
@@ -61,39 +114,77 @@ def __new__(cls, other, channel=None, *args, **kwargs):
61114 or set in the can.rc config.
62115
63116 """
117+
118+ # Figure out the configuration
64119 config = load_config (config = {
65- 'interface' : kwargs .get ('bustype' ),
120+ 'interface' : kwargs .get ('bustype' , kwargs . get ( 'interface' ) ),
66121 'channel' : channel
67122 })
68123
124+ # remove the bustype & interface so it doesn't get passed to the backend
69125 if 'bustype' in kwargs :
70- # remove the bustype so it doesn't get passed to the backend
71126 del kwargs ['bustype' ]
72- interface = config [ 'interface' ]
73- channel = config [ 'channel ' ]
127+ if 'interface' in kwargs :
128+ del kwargs [ 'interface ' ]
74129
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 :
80164
81165 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
87172 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
95186
96- return cls ( channel , ** kwargs )
187+ return result
97188
98189
99190class CyclicSendTask (CyclicSendTaskABC ):
0 commit comments