[英]socketcan J1939 filter use in python
在 Python 中,我正在嘗試使用 linux kernel 文檔中提到的 J1939 過濾: https://www.kernel.org/doc/html/latest.networking/j1939.88747064
以下代碼在 setsockopt() 行(設置過濾器)失敗:
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()
首先,kernel 文檔提到使用 SOL_CAN_J1939 作為第一個參數。 但是 socket.SOL_CAN_J1939 不存在於套接字 package 中。因此查看此位置的代碼我能夠理解此 int 值應為 107: http://socket-can.996257.n3.nabble.com/RFC -v3-0-6-CAN-添加-SAE-J1939-協議-td7571.html
至於 setsockopt() 第三個參數,我打包了過濾器以匹配 j1939_filter 結構(26 字節,如上一個鏈接的代碼中所述)。 這類似於在 can.interfaces.socketcan.utils 中為原始 CAN 所做的。
我做錯了什么導致 setsockopt() 失敗?
第一個問題是 struct.pack 格式 (can_filter_fmt) 錯誤。 我首先假設 kernel j1939_filter 結構大小是成員的總和。 這是錯誤的,因為編譯器添加了填充。 這可以添加到 struct.pack 格式為 x,例如 2Q2I2B6x。 請參閱為什么結構的 sizeof 不等於每個成員的 sizeof 之和?
第二個問題是 can_filter_fmt 沒有打包為 2Q2B2I,而是打包為 2Q2I2B6x(addr 成員在中間)。
至於 SOL_CAN_J1939 我是正確的,需要在文件中創建,因為它還沒有在 package 中。
最終代碼如下:
#!/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()
謝謝你。
要使 J1939 與 SocketCAN 一起工作,您需要兩件事:
can-1939 測試:
如果你安裝 can-utils 並且在 sudo modprobe can-j1939 之后你得到的都是致命錯誤,或者如果你從 can-utils 啟動 testj1939 並且你得到協議不支持的錯誤,那么這意味着 can-j1939 沒有啟用你的 kernel 你需要手動編譯它。
以下是我在 Debian 10 kernel 中啟用 can-j1939 的說明:
https://github.com/linux-can/can-utils/blob/master/can-j1939-install-kernel-module.md
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.