Python 如何在没有任何CAN硬件的两台Linux机器之间发送和接收CAN消息?

Python 如何在没有任何CAN硬件的两台Linux机器之间发送和接收CAN消息?,python,linux,usb,wifi,can-bus,Python,Linux,Usb,Wifi,Can Bus,我一直在尝试在我的笔记本电脑(Ubuntu20VirtualBox)和RaspberryPI(Ubuntu20)之间建立CAN通信,而不使用任何CAN硬件,因为这样在模拟环境中无法获得CAN消息。我想通过wifi或USB将CAN数据作为有效负载发送。然后,我的python模拟环境应该能够将这些消息解释为CAN消息并适当地转发 我曾尝试过socketcan中的vcan,但它只能在同一台Linux机器的两个终端之间工作。有人建议我看一下slcan。除了使用实际的CAN硬件,似乎没有其他选择。我在任何

我一直在尝试在我的笔记本电脑(Ubuntu20VirtualBox)和RaspberryPI(Ubuntu20)之间建立CAN通信,而不使用任何CAN硬件,因为这样在模拟环境中无法获得CAN消息。我想通过wifi或USB将CAN数据作为有效负载发送。然后,我的python模拟环境应该能够将这些消息解释为CAN消息并适当地转发

我曾尝试过
socketcan
中的
vcan
,但它只能在同一台Linux机器的两个终端之间工作。有人建议我看一下
slcan
。除了使用实际的CAN硬件,似乎没有其他选择。我在任何地方都找不到任何教程或其他帮助

如果有人能建议如何在没有任何can硬件的情况下通过wifi或USB在2台Linux机器之间发送和接收can消息,我将不胜感激

我尝试过的Python源代码:

import sys
import socket
import argparse
import struct
import errno


class CANSocket(object):
  FORMAT = "<IB3x8s"
  FD_FORMAT = "<IB3x64s"
  CAN_RAW_FD_FRAMES = 5

  def __init__(self, interface=None):
    self.sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
    if interface is not None:
      self.bind(interface)

  def bind(self, interface):
    self.sock.bind((interface,))
    self.sock.setsockopt(socket.SOL_CAN_RAW, self.CAN_RAW_FD_FRAMES, 1)

  def send(self, cob_id, data, flags=0):
    cob_id = cob_id | flags
    can_pkt = struct.pack(self.FORMAT, cob_id, len(data), data)
    self.sock.send(can_pkt)

  def recv(self, flags=0):
    can_pkt = self.sock.recv(72)

    if len(can_pkt) == 16:
      cob_id, length, data = struct.unpack(self.FORMAT, can_pkt)
    else:
      cob_id, length, data = struct.unpack(self.FD_FORMAT, can_pkt)

    cob_id &= socket.CAN_EFF_MASK
    return (cob_id, data[:length])


def format_data(data):
    return ''.join([hex(byte)[2:] for byte in data])


def generate_bytes(hex_string):
    if len(hex_string) % 2 != 0:
      hex_string = "0" + hex_string

    int_array = []
    for i in range(0, len(hex_string), 2):
        int_array.append(int(hex_string[i:i+2], 16))

    return bytes(int_array)


def send_cmd(args):
    try:
      s = CANSocket(args.interface)
    except OSError as e:
      sys.stderr.write('Could not send on interface {0}\n'.format(args.interface))
      sys.exit(e.errno)

    try:
      cob_id = int(args.cob_id, 16)
    except ValueError:
      sys.stderr.write('Invalid cob-id {0}\n'.format(args.cob_id))
      sys.exit(errno.EINVAL)

    s.send(cob_id, generate_bytes(args.body), socket.CAN_EFF_FLAG if args.extended_id else 0)


def listen_cmd(args):
    try:
      s = CANSocket(args.interface)
    except OSError as e:
      sys.stderr.write('Could not listen on interface {0}\n'.format(args.interface))
      sys.exit(e.errno)

    print('Listening on {0}'.format(args.interface))

    while True:
        cob_id, data = s.recv()
        print('%s %03x#%s' % (args.interface, cob_id, format_data(data)))


def parse_args():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    send_parser = subparsers.add_parser('send', help='send a CAN packet')
    send_parser.add_argument('interface', type=str, help='interface name (e.g. vcan0)')
    send_parser.add_argument('cob_id', type=str, help='hexadecimal COB-ID (e.g. 10a)')
    send_parser.add_argument('body', type=str, nargs='?', default='',
      help='hexadecimal msg body up to 8 bytes long (e.g. 00af0142fe)')
    send_parser.add_argument('-e', '--extended-id', action='store_true', default=False,
      help='use extended (29 bit) COB-ID')
    send_parser.set_defaults(func=send_cmd)

    listen_parser = subparsers.add_parser('listen', help='listen for and print CAN packets')
    listen_parser.add_argument('interface', type=str, help='interface name (e.g. vcan0)')
    listen_parser.set_defaults(func=listen_cmd)

    return parser.parse_args()


def main():
    args = parse_args()
    args.func(args)


if __name__ == '__main__':
    main()
导入系统 导入套接字 导入argparse 导入结构 输入错误号 类CANSocket(对象): FORMAT=“CAN是一种广播协议,而不是TCP之类的连接协议或UDP之类的数据报协议。但是,如果您想要抽象硬件,但仍然模拟CAN的高层行为,我建议最好的方法是使用UDP(但是,@M.Spiller建议的TCP也可以工作,只意味着处理UDP不需要麻烦的接受连接),并发送和接收与CAN帧相同字节结构的数据包。您还需要考虑过滤器和错误帧

如果要在linux上使用socketcan,您的UDP数据包应该如下所示:

struct {
    uint16_t id;       /* CAN ID of the frame */
    uint8_t dlc;       /* frame payload length in bytes */
    uint8_t data[8];   /* CAN frame payload */
} simulated_udp_can_frame;

您应该阅读以了解更多详细信息。

不确定您想做什么。为什么不简单地打开两台机器之间的TCP套接字并发送日期?TCP协议不关心有效负载是CAN消息还是其他内容。好的。我正在为Pi代表Automot的特定汽车用例构建一个虚拟平台ive ECU。这就是为什么我必须使用CAN,因为ECU通过CAN相互交互。仍然没有得到它。是的,ECU交换数据;是的,他们可能在车内使用CAN。你想模拟什么?交换的数据?计时?CAN协议使用定义良好的物理层,如果没有适当的硬件,就无法模拟e、 那么,您将模拟什么?我将模拟防抱死制动系统用例。1 RPI应通过CAN将车速值发送到笔记本电脑。但为什么必须是“通过CAN”"。如果您想模拟制动系统,为什么发送速度值还不够?因此,如果没有CAN硬件,您将错过许多需要做的事情,例如设置比特率等。但是您可以“假装”CAN硬件在那里,并且您正在从一端发送帧到另一端。但实际上,您将发送帧UDP。有关帧的重要内容是设置过滤器、消息ID和位顺序,因为这对于真正的CAN软件至关重要。您可以从socketCAN文档中了解这一点。您可以编写代码,使这些内容:过滤器、帧和ID与真正的硬件用途相同。是的。CAN套接字类似于:socket(PF_CAN、SOCK_RAW、CAN_RAW);UDP套接字是套接字(AF_INET、SOCK_DGRAM、IPPROTO_UDP);但它们都是套接字。使用CAN套接字可以读写;使用UDP可以发送和接收;但是数据的布局可以是相同的,如前所述。如果使用相同的wifi,则两台linux机器应该位于同一子网中,或者至少每台机器上的ip地址应该可以与另一台机器访问。如果没有CAN硬件,则不能进入g获取CAN套接字。该套接字由内核创建,需要一个驱动程序和一个ip接口,该接口将被称为can0或类似于eth0的类似接口。对于您所设计的环境,在没有CAN硬件的两台计算机之间;是的,完全正确。使用UDP和假装。如果您可以在一台计算机上演示,您可以d使用vcan。如果你在一台电脑上有一些插入式CAN硬件或CAN仿真器,那么你就可以使用CAN插座。但是两台电脑之间没有硬件?你必须假装UDP是最简单的方法。兄弟。我在这里猜,但我认为你是CAN的新手。从应用软件的角度来看(顺便说一句,我在汽车硬件/软件设计公司工作),关于CAN,你必须了解的是它有一个面向位的消息结构,即你必须以位而不是字节来考虑数据。从维基百科关于CAN的文章开始,特别是关于框架布局的部分。