0

I'm trying to implement a notifier for for the python-can (4.0.0) following the exact same approach as in here but I'm getting the following error:

Exception in thread can.notifier for bus "socketcan channel 'can0'":
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.7/dist-packages/can/notifier.py", line 121, in _rx_thread
    self._on_message_received(msg)
  File "/usr/local/lib/python3.7/dist-packages/can/notifier.py", line 143, in _on_message_received
    res = cast(Union[None, Optional[Awaitable[Any]]], callback(msg))
TypeError: 'NoneType' object is not callables

My code:

import os
import can

os.system('sudo ip link set can0 up type can bitrate 500000')

bus = can.interface.Bus(channel = 'can0', bustype = 'socketcan')

def parseData(can):
        SingleCanFrame = can.Message

notifier = can.Notifier(bus,[parseData(can)])

while(1):
        continue

os.system('sudo ifconfig can0 down')

I don't really understand what I'm doing wrong and the python-can documentation on the notifier is not very helpful either.

Sebastião
  • 160
  • 13
  • Your `parseData` function does not return anything. You should not CALL the function, you should pass the function objects: `notifier = can.Notifier(bus, [parseData])`. But you'll need to DO something with the message when you get it. – Tim Roberts May 05 '22 at 21:59
  • Does it need to? The question that I linked also does not return anything on the callback. My idea was to do some decisions inside the parse data, or store the data somewhere. If it returned something what would be the use for that return value? – Sebastião May 05 '22 at 22:00
  • My GUESS is the example you copied from created a `parseData` class, where you would need to create an object of that class. If you're passing a function, just pass the name of the function. No parens, no arguments. – Tim Roberts May 05 '22 at 22:02
  • According to [this](https://python-can.readthedocs.io/en/master/_modules/can/notifier.html) ```:param listeners: An iterable of :class:`~can.Listener` or callables that receive a :class: `~can.Message` and return nothing.``` – Sebastião May 05 '22 at 22:14
  • 1
    Yes, it doesn't have to return anything, I guessed wrong about that. But you DO need to pass the function, not CALL the function. Pass `[parsedData]`, not `[parsedData(can)]`. – Tim Roberts May 06 '22 at 02:54
  • But that is what I have on my question. And is giving me the error. I will edit the question with a code that can be tested even with CAN Hardware. – Sebastião May 06 '22 at 06:12
  • 1
    No, sir, it is NOT. You have `notifier = can.Notifier(bus,[parsedData(can)])`. That's WRONG. You need to delete the `(can)` from that. – Tim Roberts May 06 '22 at 06:20
  • Sorry. I miss read your comment. You are right! Thank you ver much! – Sebastião May 06 '22 at 06:24

2 Answers2

1

I'm posting my contribution since we don't have many info regarding this subject on the internet, follow my script here to help the community. I'm using a industrial computer with a CAN driver to collet CAN J1939 data, these data are generated by a VECTOR device using the CANnalyzer SW. This code is working very well to me and collecting data in a good sample rate (ms).

import cantools
import can
from ctypes import *
import logging
import time

# Logger config
logger = logging.getLogger(__name__)

# CAN filter
can_filter = [
    {"can_id": 0xCF00400, "can_mask": 0xFFFFFFF, "extended": True},
    {"can_id": 0xCF00300, "can_mask": 0xFFFFFFF, "extended": True},
    {"can_id": 0x10FD5200, "can_mask": 0x1FFFFFFF, "extended": True},
    {"can_id": 0x1CFE8800, "can_mask": 0x1FFFFFFF, "extended": True},
]

def main():
    """main _summary_"""
    can_bus = can.interface.Bus(bustype="socketcan", channel="can1", bitrate=500000, can_filters=can_filter)  # type: ignore
    log_filename = time.strftime("%d-%m-%Y_%H-%M", time.localtime())
    listener = can.Logger(filename=f"can_listener_{log_filename}.log", mode="a")  # type: ignore
    notifier = can.Notifier(can_bus, [listener])  # type: ignore

    try:
        while True:
            continue
    except Exception as e:
        print(e)
    finally:
        notifier.stop()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("can reciever stopped")
    except Exception as e:
        print(e)
0

Like @TimRoberts said I was passing the function in the wrong way.

Here is a code that works and can be tested even without the CAN Hardware.

from time import sleep
import can

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

msg = can.Message(arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7])

def parseData(can):
        print( can.arbitration_id )

notifier = can.Notifier(bus2,[parseData])

while (1):
    bus1.send(msg)
    sleep(1)
Sebastião
  • 160
  • 13