首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >socketcan J1939过滤器在python中使用

socketcan J1939过滤器在python中使用
EN

Stack Overflow用户
提问于 2021-01-16 06:00:19
回答 2查看 217关注 0票数 0

在Python语言中,我尝试使用linux内核文档中提到的J1939过滤:https://www.kernel.org/doc/html/latest/networking/j1939.html

以下代码在setsockopt()行(设置过滤器)失败:

代码语言:javascript
复制
import socket
import struct

def pack_J1939_filters(can_filters):
    can_filter_fmt = "=" + "2Q2B2I" * len(can_filters)
    filter_data = []
    for can_filter in can_filters:
        name = can_filter['name']
        name_mask = can_filter['name_mask']
        addr = can_filter['addr']
        addr_mask = can_filter['addr_mask']
        pgn = can_filter['pgn']
        pgn_mask = can_filter['pgn_mask']

        filter_data.append(name)
        filter_data.append(name_mask)
        filter_data.append(addr)
        filter_data.append(addr_mask)
        filter_data.append(pgn)
        filter_data.append(pgn_mask)

    return struct.pack(can_filter_fmt, *filter_data)


s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939) 

interface = "vcan0"

src_name = socket.J1939_NO_NAME
src_pgn = socket.J1939_NO_PGN 
src_addr = 0x81 
src_sck_addr = (interface, src_name, src_pgn, src_addr)
s.bind(src_sck_addr)

filters = [{"name": 0, "name_mask":0, "addr":0, "addr_mask":0, "pgn": 0, "pgn_mask": 0}]
packed_filters = pack_J1939_filters(filters)

# socket.SOL_CAN_J1939 does not seem to exist
SOL_CAN_BASE = 100
CAN_J1939 = 7
SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939

s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters) 

s.recvfrom(128)
s.close()

首先,内核文档提到使用SOL_CAN_J1939作为第一个参数。但是,socket.SOL_CAN_J1939不存在于套接字包中。看一下这个位置的代码,我就能理解这个int值应该是107:http://socket-can.996257.n3.nabble.com/RFC-v3-0-6-CAN-add-SAE-J1939-protocol-td7571.html

至于setsockopt()的第三个参数,我打包了过滤器以匹配j1939_filter结构(如前面链接的代码中所述的26个字节)。这类似于在can.interfaces.socketcan.utils中对原始罐头所做的操作。

我做错了什么导致setsockopt()失败?

EN

回答 2

Stack Overflow用户

发布于 2021-01-17 01:41:47

第一个问题是struct.pack格式(can_filter_fmt)错误。我首先假设内核j1939_filter结构大小是成员的总和。这是错误的,因为编译器添加了填充。这可以作为x添加到struct.pack格式中,例如2Q2I2B6x。请参阅Why isn't sizeof for a struct equal to the sum of sizeof of each member?

第二个问题是,can_filter_fmt不是打包为2Q2B2I,而是2Q2I2B6x ( addr成员在中间)。

至于SOL_CAN_J1939,我是正确的,需要在文件中创建,因为它还不在包中。

最终代码如下:

代码语言:javascript
复制
#!/usr/bin/env python3

import socket
import struct

def pack_J1939_filters(can_filters=None):
    if can_filters is None:
        # Pass all messages
        can_filters = [{}]

    can_filter_fmt = "=" + "2Q2I2B6x" * len(can_filters)
    filter_data = []
    for can_filter in can_filters:
        if 'name' in can_filter:
            name = can_filter['name']
        else:
            name = 0

        if 'name_mask' in can_filter:
            name_mask = can_filter['name_mask']
        else:
            name_mask = 0

        if 'pgn' in can_filter:
            pgn = can_filter['pgn']
        else:
            pgn = 0

        if 'pgn_mask' in can_filter:
            pgn_mask = can_filter['pgn_mask']
        else:
            pgn_mask = 0
        
        if 'addr' in can_filter:
            addr = can_filter['addr']
        else:
            addr = 0

        if 'addr_mask' in can_filter:
            addr_mask = can_filter['addr_mask']
        else:
            addr_mask = 0

        filter_data.append(name)
        filter_data.append(name_mask)
        filter_data.append(pgn)
        filter_data.append(pgn_mask)
        filter_data.append(addr)
        filter_data.append(addr_mask)

    return struct.pack(can_filter_fmt, *filter_data)

def print_msg(data, sck_addr):
    print(f"SA:{hex(sck_addr[3])} PGN:{hex(sck_addr[2])}")

    for j in range(len(data)):
        if j % 8 == 0 and j != 0:
            print()

        if j % 8 == 0:
            print(f"bytes {j} to {j+7}: ", end="")

        print(f"{hex(data[j])} ", end="")

    print()
    print()
    

def main():
    s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939) 

    # allows to receive broadcast messages
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    
    interface = "vcan0"

    src_name = socket.J1939_NO_NAME
    src_pgn = socket.J1939_NO_PGN # always no PGN for source, unless filtering is needed
    src_addr = 0x81 # recvfrom() will not return destination specific messages for other addresses
    src_sck_addr = (interface, src_name, src_pgn, src_addr)
    s.bind(src_sck_addr)

    packed_filters = pack_J1939_filters()

    SOL_CAN_BASE = 100
    CAN_J1939 = 7
    SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939

    s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters) 

    (recv_data, recv_sck_addr) = s.recvfrom(128)
    print_msg(recv_data, recv_sck_addr)

    s.close()


if __name__ == "__main__":
    main()

谢谢。

票数 0
EN

Stack Overflow用户

发布于 2021-06-10 19:21:07

要让J1939与SocketCAN协同工作,您需要两件事:

  1. 内核5.4+
  2. can-j1939内核模块已启用

can-1939测试:

如果你安装了can-utils,在sudo modprobe -J1939之后,你得到的是致命错误,或者如果你从can-utils启动testj1939,你得到的错误是协议不受支持,那么这意味着你的内核中没有启用can-j1939,你需要手动编译它。

以下是我在Debian 10内核中启用can-j1939的说明:

https://github.com/linux-can/can-utils/blob/master/can-j1939-install-kernel-module.md

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65744096

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档