pythonpython-can

Python-can bus.recv missing some messages


I am using python and python-can to send messages\receive and process messages from a CAN bus which I create like this:

from can.interface import Bus

bus = Bus(interface="pcan", channel="EXT_CAN", bitrate=500000, receive_own_messages=True)

The bus object is then passed down to a class that connects to that bus and implements the sending / processing:

def connect(self, *args, **kwargs):
        """Connects to bus. Expects "bus" as keyword argument."""        
        self._bus = kwargs.get("bus")

The class also implements a method for waiting for a specific message:

def wait_for_message(self, can_id: int, timeout: int):        
        self.time_start_listen = time.time()
        expected_message = None
        while time.time() - self.time_start_listen < timeout:
            msg = self._bus.recv(timeout=1.0)
            if msg.arbitration_id == can_id:
                expected_message = msg
                break
        assert expected_message

While testing this method, I have noticed that sometimes, the expected message is simply not picked up and the method raises an AsserionError.

I am using PCAN Viewer in parallel and I can see the expected message on the trace, so I am certain the message is actually being sent.

For reference, this is a cyclic message, with a cycle time of 1s. There are several other cyclic messages on bus, with 20ms cycle times.

The timeout argument in the wait_for_message method is large enough to capture at least one frame of the 1s message. This also checks out on the timestamps of the messages I do receive. However, the 1s cycle time message is sometimes skipped.

I also tried replacing the

while time.time() - self.time_start_listen < timeout:
    msg = self._bus.recv(timeout=1.0)

with:

for msg in self._bus:

but the behavior is the same.

Has anyone experienced this behavior? What can be causing this? Any hints are appreciated. TIA!


Solution

  • Probably you sometimes detect it sometimes not because your listening time is just about as long as the message cycle time? You have a while loop but your listening time is still staggered into multiple 1s listening times, that might be the issue...

    I bet if you increase the timeout passed to recv to 2s, you'll always detect it at least once. But you'll still miss some of the next ones and of course if you're sharing this bus with other listeners, than that's probably not the way because you'd occupy the bus making other listeners miss messages.

    So one other thing you can try is to create a listener object instead. For example:

    import can
    
    listener = can.BufferedReader()
    bus = can.Bus(interface="pcan", channel="EXT_CAN", bitrate=500000)
    notifier = can.Notifier(bus, [listener])
    

    Then you can just loop over your buffered reader until you detect your message:

    while time.time() - time_start_listen < timeout:
        msg = listener.get_message()
        if msg is not None and msg.arbitration_id == can_id:
            expected_message = msg
            break
    

    Making it non-staggered and non-blocking for other potential listeners.