Python基于周立功盒子接收特定报文信号并实时绘制折线图(二)

在前一篇文章的基础上,本文优化了Python使用周立功盒子接收CAN报文并实时绘制折线图的代码。通过采用生产者-消费者模型,解决了在同一线程中处理数据和绘图可能导致的问题。主要改进包括:将主线程作为生产者,直接处理数据;在消费者线程中先缓存数据,然后在主线程中刷新matplotlib图表。
摘要由CSDN通过智能技术生成

Python基于周立功盒子接收特定报文信号并实时绘制折线图(二)

一、背景
    根据在上一篇文件Python基于周立功盒子接收特定报文信号并实时绘制折线图(一)的基础上需要做一些优化,原因是,因为取数据和画图都在同一个线程中进行,这有可能导致程序出现问题,所以想到了使用消费者和开发者模型来优化一下代码

二、实现难点

  • 由于我们调用周立功盒子使用的是ZLG给的DLL库,生产的数据便是我们接受的CAN通道里面获取的数据,必然要掉用DLL库,但是生产者的线程中,无法直接将第三方库的句柄当做参数传进去使用;;
  • 我们想要在消费者模型中使用matplotlib进行画图,以便于能将接受数据和画图分开进行,但是却发现matplotlib的刷新和展示无法在子线程中调用

三、给出解决方案

  • 针对生产者模型中无法使用第三方库,那我就直接把主进程的主线程作为生产者模型的线程,换句话说,我不用单独实现创建一个线程供生产者使用,就在主线程中进行数据的接受并插入列表中,这样就解决了线程中不能直接使用第三方库的问题;
  • 针对消费者模型中无法直接使用matplotlib的刷新和展示,我便不在消费者线程中展示刷新,因为刷新前是需要数据的,需要plot将数据添加到缓存区,以便展示使用,那我就在消费者模型中拿到数据先使用plot把数据先缓存到画布上,然后再到主进程中来刷新。

四、实现

    结构跟上篇提到的Python基于周立功盒子接收特定报文信号并实时绘制折线图(一)是一样的,我们只是新改了业务代码zlg_test.py文件,以下是zlg_test.py文件的实现


import sys
from multiprocessing import JoinableQueue, Process
import time

from zlgcan import *
import threading
import matplotlib.pyplot as plt
import csv, queue

def input_thread():
    input()

def config_and_start_zlgcan(zcanlib):
    testcantype = 1  # 0:CAN; 1:canfd
    handle = zcanlib.OpenDevice(ZCAN_USBCANFD_MINI, 0, 0)
    if handle == INVALID_DEVICE_HANDLE:
        print("Open CANFD Device failed!")
        exit(0)
    print("device handle:%d." % (handle))

    info = zcanlib.GetDeviceInf(handle)
    print("Device Information:\n%s" % (info))

    # set auto send obj

    AutoCAN_A = ZCAN_AUTO_TRANSMIT_OBJ()
    AutoCAN_B = ZCAN_AUTO_TRANSMIT_OBJ()
    AutoCAN_A.enable = 1  # enable
    AutoCAN_A.index = 0
    AutoCAN_A.interval = 200  # ms
    AutoCAN_A.obj.frame.can_id = 0x100
    AutoCAN_A.obj.transmit_type = 0
    AutoCAN_A.obj.frame.eff = 0
    AutoCAN_A.obj.frame.rtr = 0
    AutoCAN_A.obj.frame.can_dlc = 8
    for j in range(AutoCAN_A.obj.frame.can_dlc):
        AutoCAN_A.obj.frame.data[j] = j

    AutoCAN_B.enable = 1  # enable
    AutoCAN_B.index = 1
    AutoCAN_B.interval = 200  # ms
    AutoCAN_B.obj.frame.can_id = 0x300
    AutoCAN_B.obj.transmit_type = 0
    AutoCAN_B.obj.frame.eff = 0
    AutoCAN_B.obj.frame.rtr = 0
    AutoCAN_B.obj.frame.can_dlc = 8
    for j in range(AutoCAN_B.obj.frame.can_dlc):
        AutoCAN_B.obj.frame.data[j] = j

    AutoCAN_B_delay = ZCANFD_AUTO_TRANSMIT_OBJ_PARAM()
    AutoCAN_B_delay.index = AutoCAN_B.index
    AutoCAN_B_delay.type = 1
    AutoCAN_B_delay.value = 100

    # Start CAN
    chn_handle = canfd_start(zcanlib, handle, 0, AutoCAN_A, AutoCAN_B, AutoCAN_B_delay)
    print("channel handle:%d." % (chn_handle))

    return handle, chn_handle


# USBCANFD-MINI Demo
def com_extract_signal(endian, bit_pos, bit_size, src_buf, factor, offset):
    byte_no = bit_pos // 8

    if (((byte_no * 8) + 8) - bit_pos) <= bit_size:
        total_bits_copied = ((byte_no * 8) + 8) - bit_pos
        a_data = src_buf[byte_no] >> (bit_pos - (byte_no * 8))
    else:
        a_data = src_buf[byte_no] >> (bit_pos - (byte_no * 8))
        a_data = a_data & (~(0xFF << bit_size))
        total_bits_copied = bit_size

    while total_bits_copied < bit_size:
        bits_left = bit_size - total_bits_copied
        if endian != 1:  # big_endian == 1
            byte_no += 1
        else:
            byte_no -= 1

        if bits_left >= 8:
            a_data = (src_buf[byte_no] << total_bits_copied) | a_data
            total_bits_copied = total_bits_copied + 8
        else:
            a_data = ((src_buf[byte_no] & (0xFF >> (8 - bits_left))) << total_bits_copied) | a_data
            total_bits_copied = total_bits_copied + bits_left

    return a_data * factor + offset


def creat_fig():
    fig = plt.figure('CPU Load Monitor', figsize=(16, 9))
    fig.suptitle(' J6VL3 ADU CPU Load Monitor ', fontsize='x-large')

    ax = fig.add_subplot(6, 1, 1)
    ax.plot([], [], 'bo-', markersize=1, linewidth=0.5, label='Core0 Cpuload')
    ax.legend()

    bx = fig.add_subplot(6, 1, 2)
    bx.plot([], [], 'ro-', markersize=1, linewidth=0.5, label='Core1 Cpuload')
    bx.legend()

    cx = fig.add_subplot(6, 1, 3)
    cx.plot([], [], 'go-', markersize=1, linewidth=0.5, label='Core2 Cpuload')
    cx.legend()

    dx = fig.add_subplot(6, 1, 4)
    dx.plot([], [], 'co-', markersize=1, linewidth=0.5, label='Core3 Cpuload')
    dx.legend()

    ex = fig.add_subplot(6, 1, 5)
    ex.plot([], [], 'mo-', markersize=1, linewidth=0.5, label='Core4 Cpuload')
    ex.legend()

    fx = fig.add_subplot(6, 1, 6)
    fx.plot([], [], 'yo-', markersize=1, linewidth=0.5, label='Core5 Cpuload')
    fx.legend()
    fx.set_xlabel("Sampling: 1Hz")

    return ax, bx, cx, dx, ex, fx


def get_mess_and_parse_sig(zcanlib, chn_handle):
    rcv_canfd_num = zcanlib.GetReceiveNum(chn_handle, ZCAN_TYPE_CANFD)
    if rcv_canfd_num:
        # print("Receive CANFD message number:%d" % rcv_canfd_num)
        rcv_canfd_msgs, rcv_canfd_num = zcanlib.ReceiveFD(chn_handle, rcv_canfd_num, 1000)

        for i in range(rcv_canfd_num):
            if rcv_canfd_msgs[i].frame.can_id == 0x101:
                return rcv_canfd_msgs[i]


def get_sig_values(mess_info):
    core0_cpuload = com_extract_signal(0, 88, 16, mess_info.frame.data, 0.1, 0)
    core1_cpuload = com_extract_signal(0, 104, 16, mess_info.frame.data, 0.1, 0)
    core2_cpuload = com_extract_signal(0, 120, 16, mess_info.frame.data, 0.1, 0)
    core3_cpuload = com_extract_signal(0, 136, 16, mess_info.frame.data, 0.1, 0)
    core4_cpuload = com_extract_signal(0, 152, 16, mess_info.frame.data, 0.1, 0)
    core5_cpuload = com_extract_signal(0, 168, 16, mess_info.frame.data, 0.1, 0)
    data_list = list([core0_cpuload, core1_cpuload, core2_cpuload, core3_cpuload, core4_cpuload, core5_cpuload])
    return data_list


def put_data_in_y_list(y, data):
    y[0].append(data[0])
    y[1].append(data[1])
    y[2].append(data[2])
    y[3].append(data[3])
    y[4].append(data[4])
    y[5].append(data[5])


def draw_and_updata_figs(ax, bx, cx, dx, ex, fx, x, y, plt):
    ax.plot(x, y[0], 'bo-', markersize=1, linewidth=0.5)
    bx.plot(x, y[1], 'ro-', markersize=1, linewidth=0.5)
    cx.plot(x, y[2], 'go-', markersize=1, linewidth=0.5)
    dx.plot(x, y[3], 'co-', markersize=1, linewidth=0.5)
    ex.plot(x, y[4], 'mo-', markersize=1, linewidth=0.5)
    fx.plot(x, y[5], 'yo-', markersize=1, linewidth=0.5)


class Consumer(threading.Thread):
    def __init__(self, q, ax, bx, cx, dx, ex, fx, y, plt):
        # 从python3 开始,继承有了极大的改善
        super().__init__()
        # 我喜欢用单下划线去标识私有变量
        self._queue = q
        self.ax = ax
        self.bx = bx
        self.cx = cx
        self.dx = dx
        self.ex = ex
        self.fx = fx
        self.y = y
        self.plt = plt

    # 继承了Thread类后,需要重写run方法来自定义事件
    def run(self):
        x = []
        count = 0
        while True:
            # msg即是我们说的消息,也是仓库中的货物
            msg = self._queue.get()
            # 我会在生产者中加入quit关键字,保证程序能够自动退出
            if isinstance(msg, str) and msg == 'quit':
                break
            count += 1
            x.append(count)
            print(f"I'm a thread, and I received {msg}!!")
            put_data_in_y_list(self.y, msg)

            draw_and_updata_figs(self.ax, self.bx, self.cx, self.dx, self.ex, self.fx, x, self.y, self.plt)

        print('Bye byes!')


if __name__ == "__main__":
    time_max = 3600  # 秒
    time_flag = 0
    x = []
    y = [[], [], [], [], [], []]
    # 创建ZLCCAN对象
    zcanlib = ZCAN()
    # 创建zlccan句柄和通道句柄
    handle, chn_handle = config_and_start_zlgcan(zcanlib)

    q = queue.Queue()  # 创建队列

    rec_msg_thread = threading.Thread(target=input_thread)
    rec_msg_thread.start()

    # 创建六个坐标系的画布
    ax, bx, cx, dx, ex, fx = creat_fig()

    # 设置CSV文件格式并创建csv_writer
    sys_time = '{}_{}_{}_{}_{}_{}'.format(time.localtime().tm_year, time.localtime().tm_mon,
                                          time.localtime().tm_mday, time.localtime().tm_hour,
                                          time.localtime().tm_min, time.localtime().tm_sec)
    log_path = './LOG' + sys_time + '.csv'
    fp = open(log_path, 'w')
    csv_writer = csv.writer(fp)
    csv_writer.writerow(['Time', 'Core0 Cpuload', 'Core1 Cpuload', 'Core2 Cpuload',
                         'Core3 Cpuload', 'Core4 Cpuload', 'Core5 Cpuload'])

    # 初始化一个消费者实例
    worker = Consumer(q, ax, bx, cx, dx, ex, fx, y, plt)
    # 开启消费者线程
    worker.start()

    # 获取message并且解析信号值
    while True:
        rcv_canfd_num = zcanlib.GetReceiveNum(chn_handle, ZCAN_TYPE_CANFD)
        if rcv_canfd_num:
            # print("Receive CANFD message number:%d" % rcv_canfd_num)
            rcv_canfd_msgs, rcv_canfd_num = zcanlib.ReceiveFD(chn_handle, rcv_canfd_num, 1000)

            for i in range(rcv_canfd_num):
                if rcv_canfd_msgs[i].frame.can_id == 0x101:
                    time_flag += 1
                    mess_info = rcv_canfd_msgs[i]
                    if time_flag >= time_max:
                        q.put('quit')
                        worker.join()
                        rec_msg_thread.join()
                        break
                    else:
                        data_list = get_sig_values(mess_info)

                        write_data = [mess_info.timestamp] + data_list

                        csv_writer.writerow(write_data)

                        q.put(data_list)

                    plt.pause(1)  # 暂停1秒以更新图像
                    plt.ioff()
        else:
            if not rec_msg_thread.is_alive():
                break

    q.put('quit')
    worker.join()

    plt.savefig('./PNG/result_{}.png'.format(sys_time))

    plt.close()

    # Close CAN
    ret = zcanlib.ResetCAN(chn_handle)
    if ret == 1:
        print("ResetCAN success! ")
    # Close Device
    ret = zcanlib.CloseDevice(handle)
    if ret == 1:
        print("CloseDevice success! ")
    fp.close()
    rec_msg_thread.join()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_长风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值