Skip to content

Commit 441ddba

Browse files
Add virtual interface.
1 parent 2446729 commit 441ddba

File tree

5 files changed

+91
-0
lines changed

5 files changed

+91
-0
lines changed

README

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ IXXAT
8585
The Linux ECI SDK is currently unsupported.
8686
On Linux some devices are supported with socketcan.
8787

88+
virtual
89+
~~~~~~~
90+
91+
A virtual CAN bus that can be used for automatic tests. Any Bus instances
92+
connecting to the same channel (in the same python program) will get each
93+
others messages.
94+
8895
Installation
8996
------------
9097

can/interfaces/interface.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ def __new__(cls, other, channel=None, *args, **kwargs):
5353
elif interface == 'ixxat':
5454
from can.interfaces.ixxat import IXXATBus
5555
cls = IXXATBus
56+
elif interface == 'virtual':
57+
from can.interfaces.virtual import VirtualBus
58+
cls = VirtualBus
5659
else:
5760
raise NotImplementedError("CAN interface '{}' not supported".format(interface))
5861

can/interfaces/virtual.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
This module implements an OS and hardware independent
5+
virtual CAN interface for testing purposes.
6+
7+
Any VirtualBus instances connecting to the same channel
8+
will get the same messages. Sent messages will also be
9+
echoed back to the same bus.
10+
"""
11+
12+
import logging
13+
import time
14+
try:
15+
import queue as queue
16+
except ImportError:
17+
import Queue as queue
18+
from can.bus import BusABC
19+
20+
21+
logger = logging.getLogger(__name__)
22+
logger.setLevel(logging.DEBUG)
23+
24+
25+
# Channels are lists of queues, one for each connection
26+
channels = {}
27+
28+
class VirtualBus(BusABC):
29+
"""Virtual CAN bus using an internal message queue for testing."""
30+
31+
def __init__(self, channel=None, **config):
32+
self.channel_info = 'Virtual bus channel %s' % channel
33+
34+
# Create a new channel if one does not exist
35+
if channel not in channels:
36+
channels[channel] = []
37+
38+
self.queue = queue.Queue()
39+
self.channel = channels[channel]
40+
self.channel.append(self.queue)
41+
42+
def recv(self, timeout=None):
43+
try:
44+
msg = self.queue.get(block=bool(timeout), timeout=timeout)
45+
except queue.Empty:
46+
return None
47+
48+
logger.log(9, 'Received message:\n%s', msg)
49+
return msg
50+
51+
def send(self, msg):
52+
msg.timestamp = time.time()
53+
# Add message to all listening on this channel
54+
for bus_queue in self.channel:
55+
bus_queue.put(msg)
56+
logger.log(9, 'Transmitted message:\n%s', msg)
57+
58+
def shutdown(self):
59+
self.channel.remove(self.queue)

doc/interfaces.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ Found under ``can.interfaces`` are the implementations for each backend:
1111
serial
1212
ixxat
1313
pcan
14+
virtual
1415

1516
These interfaces define the low level interface to the physical controller area network.

doc/virtual.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Virtual
2+
=======
3+
4+
The virtual interface can be used as a way to write OS and driver independent
5+
tests.
6+
7+
Bus instances connecting to the same channel will share the same messages.
8+
9+
10+
.. code-block:: python
11+
12+
import can
13+
14+
bus1 = can.interface.Bus('test', bustype='virtual')
15+
bus2 = can.interface.Bus('test', bustype='virtual')
16+
17+
msg1 = can.Message(arbitration_id=0xabcde, data=[1,2,3])
18+
bus1.send(msg1)
19+
msg2 = bus2.recv()
20+
21+
assert msg1 == msg2

0 commit comments

Comments
 (0)