Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IXXAT: does not recover / no exceptions after CAN bus disconneted for a few seconds #1828

Open
IngmarPaetzold opened this issue Aug 1, 2024 · 2 comments
Labels

Comments

@IngmarPaetzold
Copy link

Describe the bug

When I unplug the CAN bus (physically pull the connector), after a few seconds, the underlying Ixxat driver is raising an exception raise VCIError("Error warning limit exceeded") that I see in the output but cannot catch in any way.
After that, the bus seems dead (no receive, no transmit). It does not recover after the physical bus is reconnected.
The bus object is a can.TreadSafeBus().
Strangely, the bus's property .state is still can.BusState.ACTIVE, and I can happily call send() (without effect, of course). Unfortunately, it does not return a status code (the error?), nor is an exception thrown.
I tried to catch: can.CanError, can.CanOperationError, can.interfaces.ixxat.exceptions.VCIError
I tried to add a custom Listener on the Notifier with method on_error(), but this is not called (in contrast to on_message_received(), so the Listener basically works).

To Reproduce

some code lines from the CanBus class:

def __init__ ...
    can.rc = can.util.load_file_config(config_file)
    self._listener = MyListener(receive_handler)
    self._bus = can.ThreadSafeBus()
    ...
    loop = asyncio.get_event_loop()
    self._notifier = can.Notifier(self._bus, [self._listener], loop=loop)

async def send_can( self, message: can.Message ) -> bool:
    print( "Bus state: " + str(self._bus.state))
    if self._bus.state == can.BusState.ACTIVE:
        self._bus.send( message )
        return True
    else:
        return False

the Listener:

class MyListener(can.Listener):
    def __init__(self, receive_handler):
        self._receive_handler = receive_handler

    def __call__(self, msg: can.Message):
        return self.on_message_received(msg)

    async def on_message_received(self, msg):
        await self._receive_handler(msg)

    async def on_error(exc):
        print( "!!!!!!!!! Listener exception" )

Expected behavior

My assumptions were:

  • nice if the driver would recover on its own once the CAN is reconnected
  • nice if I could somehow retrieve the error state in any way (return value, exception, status call). Then I could -- somehow -- actively reset the interface. Don't know how, though...
  • is that an Ixxat related issue or a general one?

Additional context

OS and version: Windows 10
Python version: 3.12.4
python-can version: 4.4.2
python-can interface/s (if applicable): IXXAT

Traceback and logs This is what I get:
Other (unknown) CAN error
CAN message flags bAddFlags/bFlags2 0x00 bflags 0x03
Other (unknown) CAN error
CAN message flags bAddFlags/bFlags2 0x00 bflags 0x03
Other (unknown) CAN error
...  (lots of these messages)
CAN message flags bAddFlags/bFlags2 0x00 bflags 0x03
Exception in thread can.notifier for bus "unknown":
Traceback (most recent call last):
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1073, in _bootstrap_inner
    self.run()
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1010, in run
Exception in callback Notifier._on_error(VCIError('Err...mit exceeded'))
handle: <Handle Notifier._on_error(VCIError('Err...mit exceeded'))>
Traceback (most recent call last):
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\asyncio\events.py", line 88, in _run     
    self._context.run(self._callback, *self._args)
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\notifier.py", line 163, in _on_error
    listener.on_error(exc)
TypeError: MyListener.on_error() takes 1 positional argument but 2 were given
    self._target(*self._args, **self._kwargs)
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\notifier.py", line 126, in _rx_thread
    if msg := bus.recv(self.timeout):
              ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\thread_safe_bus.py", line 53, in recv
    return self.__wrapped__.recv(timeout=timeout, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\bus.py", line 127, in recv
    msg, already_filtered = self._recv_internal(timeout=time_left)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\interfaces\ixxat\canlib.py", line 148, in _recv_internal
    return self.bus._recv_internal(timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\Lib\site-packages\can\interfaces\ixxat\canlib_vcinpl.py", line 731, in _recv_internal
    raise VCIError("Error warning limit exceeded")
can.interfaces.ixxat.exceptions.VCIError: Error warning limit exceeded
... (next bus.send() call:)
Bus state: BusState.ACTIVE
@IngmarPaetzold IngmarPaetzold changed the title IXXAT: does not recover / cannot catch exceptions after CAN bus broken for a few seconds IXXAT: does not recover / no exceptions after CAN bus disconneted for a few seconds Aug 1, 2024
@Tbruno25
Copy link
Contributor

Tbruno25 commented Aug 2, 2024

on_error was indeed called -- midway down your log, you can see the line

TypeError: MyListener.on_error() takes 1 positional argument but 2 were given

Upon reviewing your code, the on_error method is missing the class instance arg (self). Provide this and you should be able to successfully handle the exception.

Also, since you're using asyncio, you may just want to set the method loop.set_exception_handler(handle_exception)

@IngmarPaetzold
Copy link
Author

Thank you very much! I obviously did not see the forrest due to all these trees...
Yes, on_error() in now called. First I tried to reset the bus from there via a callback, which lead to another exception but did not solve the problem.
So it turned out that I also need loop's exception handler which catches that second exception from where I can gracefully shut down the application and let upper levels restart.
In short: Listener's on_error() is called upon the driver error, then throws another exception (without the callback needed) to trigger loop's exception handler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants