Virtual

The virtual interface can be used as a way to write OS and driver independent tests. Any VirtualBus instances connecting to the same channel (from within the same Python process) will receive each others messages.

If messages shall be sent across process or host borders, consider using the Multicast IP Interface and refer to (the next section) for a comparison and general discussion of different virtual interfaces.

Other Virtual Interfaces

There are quite a few implementations for CAN networks that do not require physical CAN hardware. This section also describes common limitations of current virtual interfaces.

Comparison

The following table compares some known virtual interfaces:

Name

Availability

Applicability

Implementation

Within Process

Between Processes

Via (IP) Networks

Without Central Server

Transport Technology

Serialization Format

virtual (this)

included

Singleton & Mutex (reliable)

none

udp_multicast (doc)

included

UDP via IP multicast (unreliable)

custom using msgpack

christiansandberg/ python-can-remote

external

Websockets via TCP/IP (reliable)

custom binary

windelbouwman/ virtualcan

external

ZeroMQ via TCP/IP (reliable)

custom binary 1

1

The only option in this list that implements interoperability with other languages out of the box. For the others (except the first intra-process one), other programs written in potentially different languages could effortlessly interface with the bus once they mimic the serialization format. The last one, however, has already implemented the entire bus functionality in C++ and Rust, besides the Python variant.

Common Limitations

Guaranteed delivery and message ordering is one major point of difference: While in a physical CAN network, a message is either sent or in queue (or an explicit error occurred), this may not be the case for virtual networks. The udp_multicast bus for example, drops this property for the benefit of lower latencies by using unreliable UDP/IP instead of reliable TCP/IP (and because normal IP multicast is inherently unreliable, as the recipients are unknown by design). The other three buses faithfully model a physical CAN network in this regard: They ensure that all recipients actually receive (and acknowledge each message), much like in a physical CAN network. They also ensure that messages are relayed in the order they have arrived at the central server and that messages arrive at the recipients exactly once. Both is not guaranteed to hold for the best-effort udp_multicast bus as it uses UDP/IP as a transport layer.

Central servers are, however, required by interfaces 3 and 4 (the external tools) to provide these guarantees of message delivery and message ordering. The central servers receive and distribute the CAN messages to all other bus participants, unlike in a real physical CAN network. The first intra-process virtual interface only runs within one Python process, effectively the Python instance of VirtualBus acts as a central server. Notably the udp_multicast bus does not require a central server.

Arbitration and throughput are two interrelated functions/properties of CAN networks which are typically abstracted in virtual interfaces. In all four interfaces, an unlimited amount of messages can be sent per unit of time (given the computational power of the machines and networks that are involved). In a real CAN/CAN FD networks, however, throughput is usually much more restricted and prioritization of arbitration IDs is thus an important feature once the bus is starting to get saturated. None of the interfaces presented above support any sort of throttling or ID arbitration under high loads.

Example

import can

bus1 = can.interface.Bus('test', bustype='virtual')
bus2 = can.interface.Bus('test', bustype='virtual')

msg1 = can.Message(arbitration_id=0xabcde, data=[1,2,3])
bus1.send(msg1)
msg2 = bus2.recv()

#assert msg1 == msg2
assert msg1.arbitration_id == msg2.arbitration_id
assert msg1.data == msg2.data
assert msg1.timestamp != msg2.timestamp
import can

bus1 = can.interface.Bus('test', bustype='virtual', preserve_timestamps=True)
bus2 = can.interface.Bus('test', bustype='virtual')

msg1 = can.Message(timestamp=1639740470.051948, arbitration_id=0xabcde, data=[1,2,3])

# Messages sent on bus1 will have their timestamps preserved when received
# on bus2
bus1.send(msg1)
msg2 = bus2.recv()

assert msg1.arbitration_id == msg2.arbitration_id
assert msg1.data == msg2.data
assert msg1.timestamp == msg2.timestamp

# Messages sent on bus2 will not have their timestamps preserved when
# received on bus1
bus2.send(msg1)
msg3 = bus1.recv()

assert msg1.arbitration_id == msg3.arbitration_id
assert msg1.data == msg3.data
assert msg1.timestamp != msg3.timestamp

Bus Class Documentation

class can.interfaces.virtual.VirtualBus(channel=None, receive_own_messages=False, rx_queue_size=0, preserve_timestamps=False, **kwargs)[source]

A virtual CAN bus using an internal message queue. It can be used for example for testing.

In this interface, a channel is an arbitrary object used as an identifier for connected buses.

Implements can.BusABC._detect_available_configs(); see can.VirtualBus._detect_available_configs() for how it behaves here.

Note

The timeout when sending a message applies to each receiver individually. This means that sending can block up to 5 seconds if a message is sent to 5 receivers with the timeout set to 1.0.

Warning

This interface guarantees reliable delivery and message ordering, but does not implement rate limiting or ID arbitration/prioritization under high loads. Please refer to the section Other Virtual Interfaces for more information on this and a comparison to alternatives.

Construct and open a CAN bus instance of the specified type.

Subclasses should call though this method with all given parameters as it handles generic tasks like applying filters.

Parameters
  • channel (Optional[Any]) – The can interface identifier. Expected type is backend dependent.

  • can_filters – See set_filters() for details.

  • kwargs (dict) – Any backend dependent configurations are passed in this dictionary

Raises
  • ValueError – If parameters are out of range

  • can.CanInterfaceNotImplementedError – If the driver cannot be accessed

  • can.CanInitializationError – If the bus cannot be initialized

send(msg, timeout=None)[source]

Transmit a message to the CAN bus.

Override this method to enable the transmit path.

Parameters
  • msg (Message) – A message object.

  • timeout (Optional[float]) – If > 0, wait up to this many seconds for message to be ACK’ed or for transmit queue to be ready depending on driver implementation. If timeout is exceeded, an exception will be raised. Might not be supported by all interfaces. None blocks indefinitely.

Raises

can.CanOperationError – If an error occurred while sending

Return type

None

shutdown()[source]

Called to carry out any interface specific cleanup required in shutting down a bus.

Return type

None