|
9 | 9 |
|
10 | 10 | from __future__ import absolute_import |
11 | 11 |
|
| 12 | +import sys |
12 | 13 | import importlib |
13 | 14 | from pkg_resources import iter_entry_points |
| 15 | +import logging |
14 | 16 |
|
15 | 17 | import can |
16 | 18 | from .bus import BusABC |
17 | 19 | from .broadcastmanager import CyclicSendTaskABC, MultiRateCyclicSendTaskABC |
18 | 20 | from .util import load_config |
19 | 21 |
|
| 22 | +if sys.version_info.major > 2: |
| 23 | + basestring = str |
| 24 | + |
| 25 | +log = logging.getLogger('can.interface') |
| 26 | +log_autodetect = log.getChild('detect_available_configs') |
20 | 27 |
|
21 | 28 | # interface_name => (module, classname) |
22 | 29 | BACKENDS = { |
@@ -48,10 +55,17 @@ def _get_class_for_interface(interface): |
48 | 55 | :raises: |
49 | 56 | NotImplementedError if the interface is not known |
50 | 57 | :raises: |
51 | | - ImportError if there was a problem while importing the |
52 | | - interface or the bus class within that |
| 58 | + ImportError if there was a problem while importing the |
| 59 | + interface or the bus class within that |
53 | 60 | """ |
54 | 61 |
|
| 62 | + # filter out the socketcan special case |
| 63 | + if interface == 'socketcan': |
| 64 | + try: |
| 65 | + interface = can.util.choose_socketcan_implementation() |
| 66 | + except Exception as e: |
| 67 | + raise ImportError("Cannot choose socketcan implementation: {}".format(e)) |
| 68 | + |
55 | 69 | # Find the correct backend |
56 | 70 | try: |
57 | 71 | module_name, class_name = BACKENDS[interface] |
@@ -102,18 +116,76 @@ def __new__(cls, other, channel=None, *args, **kwargs): |
102 | 116 |
|
103 | 117 | # Figure out the configuration |
104 | 118 | config = load_config(config={ |
105 | | - 'interface': kwargs.get('bustype'), |
| 119 | + 'interface': kwargs.get('bustype', kwargs.get('interface')), |
106 | 120 | 'channel': channel |
107 | 121 | }) |
108 | 122 |
|
109 | | - # remove the bustype so it doesn't get passed to the backend |
| 123 | + # remove the bustype & interface so it doesn't get passed to the backend |
110 | 124 | if 'bustype' in kwargs: |
111 | 125 | del kwargs['bustype'] |
| 126 | + if 'interface' in kwargs: |
| 127 | + del kwargs['interface'] |
112 | 128 |
|
113 | 129 | cls = _get_class_for_interface(config['interface']) |
114 | 130 | return cls(channel=config['channel'], *args, **kwargs) |
115 | 131 |
|
116 | 132 |
|
| 133 | +def detect_available_configs(interfaces=None): |
| 134 | + """Detect all configurations/channels that the interfaces could |
| 135 | + currently connect with. |
| 136 | +
|
| 137 | + This might be quite time consuming. |
| 138 | +
|
| 139 | + Automated configuration detection may not be implemented by |
| 140 | + every interface on every platform. This method will not raise |
| 141 | + an error in that case, but with rather return an empty list |
| 142 | + for that interface. |
| 143 | +
|
| 144 | + :param interfaces: either |
| 145 | + - the name of an interface to be searched in as a string, |
| 146 | + - an iterable of interface names to search in, or |
| 147 | + - `None` to search in all known interfaces. |
| 148 | + :rtype: list of `dict`s |
| 149 | + :return: an iterable of dicts, each suitable for usage in |
| 150 | + :class:`can.interface.Bus`'s constructor. |
| 151 | + """ |
| 152 | + |
| 153 | + # Figure out where to search |
| 154 | + if interfaces is None: |
| 155 | + # use an iterator over the keys so we do not have to copy it |
| 156 | + interfaces = BACKENDS.keys() |
| 157 | + elif isinstance(interfaces, basestring): |
| 158 | + interfaces = [interfaces, ] |
| 159 | + # else it is supposed to be an iterable of strings |
| 160 | + |
| 161 | + result = [] |
| 162 | + for interface in interfaces: |
| 163 | + |
| 164 | + try: |
| 165 | + bus_class = _get_class_for_interface(interface) |
| 166 | + except ImportError: |
| 167 | + log_autodetect.debug('interface "%s" can not be loaded for detection of available configurations', interface) |
| 168 | + continue |
| 169 | + |
| 170 | + # get available channels |
| 171 | + try: |
| 172 | + available = list(bus_class._detect_available_configs()) |
| 173 | + except NotImplementedError: |
| 174 | + log_autodetect.debug('interface "%s" does not support detection of available configurations', interface) |
| 175 | + else: |
| 176 | + log_autodetect.debug('interface "%s" detected %i available configurations', interface, len(available)) |
| 177 | + |
| 178 | + # add the interface name to the configs if it is not already present |
| 179 | + for config in available: |
| 180 | + if 'interface' not in config: |
| 181 | + config['interface'] = interface |
| 182 | + |
| 183 | + # append to result |
| 184 | + result += available |
| 185 | + |
| 186 | + return result |
| 187 | + |
| 188 | + |
117 | 189 | class CyclicSendTask(CyclicSendTaskABC): |
118 | 190 |
|
119 | 191 | @classmethod |
|
0 commit comments