解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题

python调用controlcan.dll实现接收报文参考文章:
实践出真知——Python周立功CAN接口收发
https://zhuanlan.zhihu.com/p/195116941

在解析接收到的报文数据时执行报错:
解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第1张图片

原出错代码:

    DevType = 4
    DevIndex = 0
    CANIndex = 0
    vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
                                 1, 0x01, 0x1C, 0)
    print("下面执行操作返回“1”表示操作成功!")
    print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
    print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
    print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
    print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
    print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
    rxdata = _RX_CAN_OBJ()
    while get_receive_num(DevType, DevIndex, CANIndex) == 0:
            continue
    print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
    receive_res = receive_msg(DevType, DevIndex, CANIndex, rxdata, 50, 0)
    if receive_res > 0:
        analyse_msg(rxdata)
    else:
        print("接收缓存区为空")
    print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))

重写controlcan.dll中接收报文函数VCI_Receive得到自定义的接收报文函数receive_msg(),函数receive_msg()定义如下:

def receive_msg(DevType, DevIndex, CANIndex, pReceive, Len=50, WaitTime=100):
    """

    :param DevType: 设备类型。对应不同的产品型号详见:适配器设备类型定义。
    :param DevIndex: 设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适
                     配器那么后面插入的这个设备索引号就是1,以此类推。
    :param CANIndex: CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
    :param pReceive: 用来接收的帧结构体VCI_CAN_OBJ数组的首指针。
    :param Len: 用来接收的帧结构体数组的长度(本次接收的最大帧数,实际返回值小于等于这个值)。
                该值为所提供的存储空间大小,适配器中为每个通道设置了2000帧的接收缓存区,用户根据
                自身系统和工作环境需求,在1到2000之间选取适当的接收数组长度。一般pReceive数组大
                小与Len都设置大于2000,如:2500为宜,可有效防止数据溢出导致地址冲突。同时每隔30ms
                调用一次VCI_Receive为宜。(在满足应用的时效性情况下,尽量降低调用VCI_Receive的频
                率,只要保证内部缓存不溢出,每次读取并处理更多帧,可以提高运行效率。)
    :param WaitTime: 保留参数,默认为0
    :return: 返回实际读取的帧数,=-1表示USB-CAN设备不存在或USB掉线。
    """
    res = canDLL.VCI_Receive(DevType, DevIndex, CANIndex, pointer(pReceive), Len, WaitTime)
    return res

使用analyse_msg函数解析一帧报文数据,函数代码如下:

def analyse_msg(vci_can_obj):
    # 解析一帧数据
    print("帧ID ", hex(vci_can_obj.ID))
    if vci_can_obj.RemoteFlag == 0:
        print("数据帧 ")
    else:
        print("远程帧 ")
    if vci_can_obj.ExternFlag == 0:
        print("标准帧 ")
    else:
        print("扩展帧 ")
    if vci_can_obj.RemoteFlag == 0:
        print("数据: ")
        print("0", bytearray(vci_can_obj.Data).hex()[0:2])
        print("1", bytearray(vci_can_obj.Data).hex()[2:4])
        print("2", bytearray(vci_can_obj.Data).hex()[4:6])
        print("3", bytearray(vci_can_obj.Data).hex()[6:8])
        print("4", bytearray(vci_can_obj.Data).hex()[8:10])
        print("5", bytearray(vci_can_obj.Data).hex()[10:12])
        print("6", bytearray(vci_can_obj.Data).hex()[12:14])
        print("7", bytearray(vci_can_obj.Data).hex()[14:16])

参看以下文章发现了接收报文解析出错的原因:报文接收函数VCI_Receive中Len定义成50了,但是pReceive参数只相当是接收一帧报文的存储数组,两者间存在冲突,导致调用解析报文函数analyse_msg()执行报错。

文章:调用ControlCAN.dll为何只能取第1笔数据??
https://bbs.csdn.net/topics/360121648

周立功controlcan.dll接口函数二次开发库文档中报文接收函数VCI_Receive中Len参数定义如下:
解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第2张图片
Len参数改成每次只接收一帧数据,执行是没有问题的:

    DevType = 4
    DevIndex = 0
    CANIndex = 0
    vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
                                 1, 0x01, 0x1C, 0)
    print("下面执行操作返回“1”表示操作成功!")
    print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
    print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
    print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
    print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
    print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
    rxdata = _RX_CAN_OBJ()
    while 1:
        while get_receive_num(DevType, DevIndex, CANIndex) == 0:
                continue
        print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
        receive_res = receive_msg(DevType, DevIndex, CANIndex, rxdata, 1, 0)
        if receive_res > 0:
            for i in range(receive_res):
                analyse_msg(rxdata)
        else:
            print("接收缓存区为空")

现在想实现每次接收缓存报文50帧,而不是每次调用receive_msg函数只能接收到一帧数据。

周立功官网给出的C#接收多帧报文的例子:


        private void timer_rec_Tick(object sender, EventArgs e)
        {
            UInt32 res = new UInt32();
            res = Device.VCI_GetReceiveNum(m_devtype, m_devind, m_canind);
            if (res == 0)
                return;
            //res = VCI_Receive(m_devtype, m_devind, m_canind, ref m_recobj[0],50, 100);

            /////////////////////////////////////
            UInt32 con_maxlen = 50;
            IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)con_maxlen);




            res = Device.VCI_Receive(m_devtype, m_devind, m_canind, pt, con_maxlen, 100);
            ////////////////////////////////////////////////////////

            String str = "";
            for (UInt32 i = 0; i < res; i++)
            {
                VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                str = "接收到数据: ";
                str += "  帧ID:0x" + System.Convert.ToString((Int32)obj.ID, 16);
                str += "  帧格式:";
                if (obj.RemoteFlag == 0)
                    str += "数据帧 ";
                else
                    str += "远程帧 ";
                if (obj.ExternFlag == 0)
                    str += "标准帧 ";
                else
                    str += "扩展帧 ";

                //////////////////////////////////////////
                if (obj.RemoteFlag == 0)
                {
                    str += "数据: ";
                    byte len = (byte)(obj.DataLen % 9);
                    byte j = 0;
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[0], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[1], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[2], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[3], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[4], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[5], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[6], 16);
                    if (j++ < len)
                        str += " " + System.Convert.ToString(obj.Data[7], 16);

                }

                listBox_Info.Items.Add(str);
                listBox_Info.SelectedIndex = listBox_Info.Items.Count - 1;
            }
            Marshal.FreeHGlobal(pt);
        }

controlcan.dll二次开发接口函数库给出的接收多帧can报文的例子:
解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第3张图片
实现一次性接收多帧报文数据的思考方向:研究ctypes的数据类型转换,报文接收函数VCI_Receive中pReceive参数需要传入一个结构体数组用来存储指定的多帧报文信息。

参考文章:
Python–ctypes(数据类型详细踩坑指南)
https://zhuanlan.zhihu.com/p/145165873

解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第4张图片
解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第5张图片
关键代码:

receive_len = 50
vci_can_array = _RX_CAN_OBJ * receive_len
vci_can_obj = vci_can_array(_RX_CAN_OBJ())
receive_res = receive_msg(DevType, DevIndex, CANIndex, vci_can_obj, receive_len, 0)

修改接收报文代码如下:

    DevType = 4
    DevIndex = 0
    CANIndex = 0
    vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
                                 1, 0x01, 0x1C, 0)
    print("下面执行操作返回“1”表示操作成功!")
    print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
    print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
    print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
    print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
    print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
    receive_len = 50
    vci_can_array = _RX_CAN_OBJ * receive_len
    vci_can_obj = vci_can_array(_RX_CAN_OBJ())
    while 1:
        while get_receive_num(DevType, DevIndex, CANIndex) == 0:
                continue
        print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
        receive_res = receive_msg(DevType, DevIndex, CANIndex, vci_can_obj, receive_len, 0)
        print("receive_res", receive_res)
        if receive_res > 0:
            for i in range(receive_res):
                analyse_msg(vci_can_obj[i])
        else:
            print("接收缓存区为空")

执行成功,执行结果部分截图:
解决python使用controlcan.dll接收报文每次只能处理一条报文信息问题_第6张图片

补充知识点:
Python调用周立功CAN接口卡接口库函数(接收数据)
在这里插入图片描述
Python读取CANalyst-II分析仪(创芯科技)接口函数
最全ctypes用法总结
https://blog.csdn.net/bianchengxueseng/article/details/115262954

你可能感兴趣的:(python,controlcan.dll,周立功can,ctypes)