Skip to content

Commit aa44f45

Browse files
authored
Choose correct socketcan interface. Closes hardbyte#120
* Improve handling of configuration. * If interface is set to socketcan in can.rc['interface'] choose which implementation to use. * Extract helper function for checking the can.rc global config. * Slightly rearrange where to find set of valid interfaces.
1 parent 215673c commit aa44f45

File tree

8 files changed

+95
-73
lines changed

8 files changed

+95
-73
lines changed

can/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
log = logging.getLogger('can')
66

7-
rc = dict(channel=0)
7+
rc = dict()
88

99

1010
class CanError(IOError):
@@ -24,4 +24,5 @@ class CanError(IOError):
2424
from can.bus import BusABC
2525
from can.notifier import Notifier
2626
from can.broadcastmanager import send_periodic, CyclicSendTaskABC, MultiRateCyclicSendTaskABC
27-
from can.interfaces import interface
27+
from can.interfaces import VALID_INTERFACES
28+
from . import interface
Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import can
22
from can.broadcastmanager import CyclicSendTaskABC, MultiRateCyclicSendTaskABC
3-
from can.util import load_config, choose_socketcan_implementation
4-
5-
VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native',
6-
'socketcan_ctypes', 'socketcan', 'usb2can', 'ixxat',
7-
'nican', 'remote', 'virtual', 'neovi'])
3+
from can.util import load_config
84

95

106
class Bus(object):
@@ -21,30 +17,25 @@ def __new__(cls, other, channel=None, *args, **kwargs):
2117
:param kwargs:
2218
Should contain a bustype key with a valid interface name.
2319
24-
:raises: NotImplementedError if the bustype isn't recognized
20+
:raises:
21+
NotImplementedError if the bustype isn't recognized
22+
:raises:
23+
ValueError if the bustype or channel isn't either passed as an argument
24+
or set in the can.rc config.
25+
2526
"""
27+
config = load_config(config={
28+
'interface': kwargs.get('bustype'),
29+
'channel': channel
30+
})
31+
2632
if 'bustype' in kwargs:
27-
can.rc['interface'] = kwargs['bustype']
33+
# remove the bustype so it doesn't get passed to the backend
2834
del kwargs['bustype']
29-
30-
if can.rc['interface'] == 'socketcan':
31-
can.rc['interface'] = choose_socketcan_implementation()
32-
33-
# Update can.rc from kwargs
34-
for kw in ('interface', 'bitrate'):
35-
if kw in kwargs:
36-
can.rc[kw] = kwargs[kw]
37-
38-
if 'interface' not in can.rc or 'channel' not in can.rc or can.rc['interface'] is None:
39-
can.log.debug("Loading default configuration")
40-
# Load defaults
41-
can.rc = load_config()
42-
43-
if can.rc['interface'] not in VALID_INTERFACES:
44-
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(can.rc['interface']))
35+
interface = config['interface']
36+
channel = config['channel']
4537

4638
# Import the correct Bus backend
47-
interface = can.rc['interface']
4839
if interface == 'kvaser':
4940
from can.interfaces.kvaser import KvaserBus
5041
cls = KvaserBus
@@ -81,8 +72,6 @@ def __new__(cls, other, channel=None, *args, **kwargs):
8172
else:
8273
raise NotImplementedError("CAN interface '{}' not supported".format(interface))
8374

84-
if channel is None:
85-
channel = can.rc['channel']
8675
return cls(channel, **kwargs)
8776

8877

@@ -91,20 +80,13 @@ class CyclicSendTask(CyclicSendTaskABC):
9180
@classmethod
9281
def __new__(cls, other, channel, *args, **kwargs):
9382

94-
# If can.rc doesn't look valid: load default
95-
if 'interface' not in can.rc or 'channel' not in can.rc:
96-
can.log.debug("Loading default configuration")
97-
can.rc = load_config()
98-
99-
print(can.rc)
100-
if can.rc['interface'] not in VALID_INTERFACES:
101-
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(can.rc['interface']))
83+
config = load_config(config={'channel': channel})
10284

10385
# Import the correct implementation of CyclicSendTask
104-
if can.rc['interface'] == 'socketcan_ctypes':
86+
if config['interface'] == 'socketcan_ctypes':
10587
from can.interfaces.socketcan.socketcan_ctypes import CyclicSendTask as _ctypesCyclicSendTask
10688
cls = _ctypesCyclicSendTask
107-
elif can.rc['interface'] == 'socketcan_native':
89+
elif config['interface'] == 'socketcan_native':
10890
from can.interfaces.socketcan.socketcan_native import CyclicSendTask as _nativeCyclicSendTask
10991
cls = _nativeCyclicSendTask
11092
# CyclicSendTask has not been fully implemented on remote interface yet.
@@ -114,33 +96,26 @@ def __new__(cls, other, channel, *args, **kwargs):
11496
# from can.interfaces.remote import CyclicSendTask as _remoteCyclicSendTask
11597
# cls = _remoteCyclicSendTask
11698
else:
117-
can.log.info("Current CAN interface doesn't support CyclicSendTask")
99+
raise can.CanError("Current CAN interface doesn't support CyclicSendTask")
118100

119-
return cls(channel, *args, **kwargs)
101+
return cls(config['channel'], *args, **kwargs)
120102

121103

122104
class MultiRateCyclicSendTask(MultiRateCyclicSendTaskABC):
123105

124106
@classmethod
125107
def __new__(cls, other, channel, *args, **kwargs):
126108

127-
# If can.rc doesn't look valid: load default
128-
if 'interface' not in can.rc or 'channel' not in can.rc:
129-
can.log.debug("Loading default configuration")
130-
can.rc = load_config()
131-
132-
print(can.rc)
133-
if can.rc['interface'] not in VALID_INTERFACES:
134-
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(can.rc['interface']))
109+
config = load_config(config={'channel': channel})
135110

136111
# Import the correct implementation of CyclicSendTask
137-
if can.rc['interface'] == 'socketcan_ctypes':
112+
if config['interface'] == 'socketcan_ctypes':
138113
from can.interfaces.socketcan.socketcan_ctypes import MultiRateCyclicSendTask as _ctypesMultiRateCyclicSendTask
139114
cls = _ctypesMultiRateCyclicSendTask
140-
elif can.rc['interface'] == 'socketcan_native':
115+
elif config['interface'] == 'socketcan_native':
141116
from can.interfaces.socketcan.socketcan_native import MultiRateCyclicSendTask as _nativeMultiRateCyclicSendTask
142117
cls = _nativeMultiRateCyclicSendTask
143118
else:
144119
can.log.info("Current CAN interface doesn't support CyclicSendTask")
145120

146-
return cls(channel, *args, **kwargs)
121+
return cls(config['channel'], *args, **kwargs)

can/interfaces/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
"""
33
Interfaces contain low level implementations that interact with CAN hardware.
44
"""
5+
6+
VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native',
7+
'socketcan_ctypes', 'socketcan', 'usb2can', 'ixxat',
8+
'nican', 'remote', 'virtual', 'neovi'])

can/interfaces/remote/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#!/usr/bin/env python
22
import logging
3-
logging.basicConfig(format='%(asctime)-15s %(message)s', level=logging.INFO)
43
import argparse
54
import can
65
from can.interfaces import remote
76

7+
logging.basicConfig(format='%(asctime)-15s %(message)s', level=logging.INFO)
8+
89

910
def main():
1011
parser = argparse.ArgumentParser(description="Remote CAN server")
@@ -22,7 +23,7 @@ def main():
2223
parser.add_argument('-i', '--interface',
2324
help='''Specify the backend CAN interface to use. If left blank,
2425
fall back to reading from configuration files.''',
25-
choices=can.interface.VALID_INTERFACES)
26+
choices=can.VALID_INTERFACES)
2627

2728
parser.add_argument('-b', '--bitrate', type=int,
2829
help='''Force to use a specific bitrate.

can/interfaces/socketcan/socketcan_ctypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class SocketcanCtypes_Bus(BusABC):
3939
channel_info = "ctypes socketcan channel"
4040

4141
def __init__(self,
42-
channel=can.rc['channel'],
42+
channel=0,
4343
receive_own_messages=False,
4444
*args, **kwargs):
4545
"""

can/io/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def main():
7979
parser.add_argument('-i', '--interface', dest="interface",
8080
help='''Specify the backend CAN interface to use. If left blank,
8181
fall back to reading from configuration files.''',
82-
choices=can.interface.VALID_INTERFACES)
82+
choices=can.VALID_INTERFACES)
8383

8484
parser.add_argument('--filter', help='''Comma separated filters can be specified for the given CAN interface:
8585
<can_id>:<can_mask> (matches when <received_can_id> & mask == can_id & mask)

can/util.py

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"""
55

66
import time
7+
8+
import can
9+
from can.interfaces import VALID_INTERFACES
10+
711
try:
812
from configparser import ConfigParser
913
except ImportError:
@@ -88,37 +92,74 @@ def load_environment_config():
8892
)
8993

9094

91-
def load_config(path=None):
95+
def load_config(path=None, config=None):
9296
"""
9397
Returns a dict with configuration details which is loaded from (in this order):
9498
95-
* Environment variables CAN_INTERFACE, CAN_CHANNEL
96-
* Config files ``/etc/can.conf`` or ``~/.can`` or ``~/.canrc``
99+
- config
100+
- can.rc
101+
- Environment variables CAN_INTERFACE, CAN_CHANNEL
102+
- Config files ``/etc/can.conf`` or ``~/.can`` or ``~/.canrc``
97103
where the latter may add or replace values of the former.
98104
99-
Interface can be kvaser, socketcan, socketcan_ctypes, socketcan_native, serial
105+
Interface can be any of the strings from ``can.VALID_INTERFACES`` for example:
106+
kvaser, socketcan, pcan, usb2can, ixxat, nican, remote, virtual.
107+
108+
.. note::
109+
110+
If you pass ``"socketcan"`` this automatically selects between the
111+
native and ctypes version.
112+
113+
:param path:
114+
Optional path to config file.
115+
:param config:
116+
A dict which may set the 'interface', and/or the 'channel', or neither.
100117
101-
The returned dictionary may look like this::
118+
:return:
119+
A config dictionary that should contain 'interface' & 'channel'::
102120
103-
{
104-
'interface': 'python-can backend interface to use',
105-
'channel': 'default channel to use',
106-
}
121+
{
122+
'interface': 'python-can backend interface to use',
123+
'channel': 'default channel to use',
124+
}
107125
108-
:param path: Optional path to config file.
126+
Note ``None`` will be used if all the options are exhausted without
127+
finding a value.
109128
"""
110-
config = load_file_config(path)
111-
config.update(load_environment_config())
129+
130+
131+
system_config = {}
132+
configs = [
133+
config,
134+
can.rc,
135+
load_environment_config,
136+
lambda: load_file_config(path)
137+
]
138+
139+
# Slightly complex here to only search for the file config if required
140+
for cfg in configs:
141+
if callable(cfg):
142+
cfg = cfg()
143+
for key in REQUIRED_KEYS:
144+
if key not in system_config and key in cfg and cfg[key] is not None:
145+
system_config[key] = cfg[key]
146+
147+
if all(k in system_config for k in REQUIRED_KEYS):
148+
break
112149

113150
# substitute None for all values not found
114151
for key in REQUIRED_KEYS:
115-
if key not in config:
116-
config[key] = None
152+
if key not in system_config:
153+
system_config[key] = None
154+
155+
if system_config['interface'] == 'socketcan':
156+
system_config['interface'] = choose_socketcan_implementation()
117157

118-
if config['interface'] == 'socketcan':
119-
config['interface'] = choose_socketcan_implementation()
158+
if system_config['interface'] not in VALID_INTERFACES:
159+
raise NotImplementedError('Invalid CAN Bus Type - {}'.format(can.rc['interface']))
120160

121-
return config
161+
can.log.debug("can config: {}".format(system_config))
162+
return system_config
122163

123164

124165
def choose_socketcan_implementation():

doc/bus.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ API
2222
:members:
2323
:special-members: __iter__
2424

25-
.. autoclass:: can.interfaces.interface.Bus
25+
.. autoclass:: can.interface.Bus
2626
:members:
2727
:special-members: __iter__
2828

0 commit comments

Comments
 (0)