一种无锁CycleBuffer

本文介绍了一种利用无锁多线程解决方案实现的CycleBuffer设计,该设计支持一个线程读取和一个线程写入操作,并且要求缓冲区长度为2的幂。文章提供了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用我的前一篇文章介绍的多线程无锁解决方案,很容易实现一个支持一个线程读,一个线程写的CycleBuffer。需要特别注意的是该CycleBuffer要求缓冲区长度为2的n次方。下面贴出代码:

class ZwAsynCount
{
public:
	ZwAsynCount(unsigned uSize)	//uSize必须为2的n次方
	{
		m_uReadCount = 0;
		m_uWriteCount = 0;
		m_uSize = uSize;
	}
	int Write(int nLen)			//返回元素位置 -1表示读失败
	{
		int nRet = -1;

		if (GetFree() >= nLen)
		{
			nRet = m_uWriteCount&(m_uSize-1);
		}
		return nRet;
	}
	void AddWrite(int nCount = 1)
	{
		m_uWriteCount += nCount;
	}
	int GetUsed()
	{
		return m_uWriteCount - m_uReadCount;
	}
	int GetFree()
	{
		return m_uSize - GetUsed();
	}
	int Read(int nLen)		//返回元素位置 -1表示写失败
	{
		int nRet = -1;

		if (GetUsed() <= nLen)
		{
			nRet = m_uReadCount&(m_uSize-1);
		}

		return nRet;
	}
	void AddRead(int nCount = 1)
	{
		m_uReadCount+= nCount;
	}
private:
	unsigned m_uSize;
	volatile unsigned m_uReadCount;
	volatile unsigned m_uWriteCount;
};

class CCycleBuffer
{
public:
	CCycleBuffer(int nSize):m_asynCount(nSize)
	{
		m_pBuf = new char[nSize];  
		m_nTalLen = nSize;
	}
	int GetFreeSpace()
	{
		return m_asynCount.GetFree();
	}
	int GetLength()
	{
		return m_asynCount.GetUsed();
	}
	~CCycleBuffer()
	{
		if (m_pBuf)
		{
			delete m_pBuf;
		}
	}
	bool Write(char* buf, int count)
	{
		bool bRet = false;
		int  nPos = m_asynCount.Write(count);

		if (nPos >=0)
		{
			if (m_nTalLen - nPos >=  count)
			{
				memcpy(m_pBuf + nPos, buf, count);
			}
			else
			{
				memcpy(m_pBuf + nPos, buf, m_nTalLen - nPos);
				memcpy(m_pBuf, buf + m_nTalLen - nPos, count - (m_nTalLen - nPos));
			}

			m_asynCount.AddWrite(count);
			bRet = true;
		}

		return bRet;
	}
	bool Read(char* buf, int count)
	{
		bool bRet = false;
		int  nPos = m_asynCount.Read(count);

		if (nPos >=0)
		{
			if (m_nTalLen - nPos >=  count)
			{
				memcpy(buf,m_pBuf + nPos,count);
			}
			else
			{
				memcpy(buf,m_pBuf + nPos, m_nTalLen - nPos);
				memcpy(buf + m_nTalLen - nPos,m_pBuf, count - (m_nTalLen - nPos));
			}

			m_asynCount.AddRead(count);
			bRet = true;
		}

		return bRet;
	}
private:
	char	*m_pBuf;
	int		m_nTalLen;
	ZwAsynCount m_asynCount;
};


-- coding: utf-8 -- import threading Form implementation generated from reading ui file ‘CanOBD.ui’ Created by: PyQt5 UI code generator 5.15.9 WARNING: Any manual changes made to this file will be lost when pyuic5 is run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QThread, pyqtSignal,QTimer from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem,QTreeWidget, QTreeWidgetItem from SerialPro import SerialPort as SerialThread from SerialPro import CanOBDItemList,CanPGNItemList import time import serial import serial.tools.list_ports import binascii import struct class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(“MainWindow”) MainWindow.resize(1220, 940) font = QtGui.QFont() font.setPointSize(12) MainWindow.setFont(font) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(“centralwidget”) self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setGeometry(QtCore.QRect(0, 0, 1041, 940)) self.tabWidget.setObjectName(“tabWidget”) self.tab = QtWidgets.QWidget() self.tab.setObjectName(“tab”) self.tableWidget = QtWidgets.QTableWidget(self.tab) self.tableWidget.setGeometry(QtCore.QRect(0, 0, 1031, 940)) self.tableWidget.setObjectName(“tableWidget”) self.tableWidget.setColumnCount(5) self.tableWidget.setRowCount(150) for num in range(0,150,1): item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(num, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() font.setKerning(False) item.setFont(font) for line in range(0,5,1): self.tableWidget.setHorizontalHeaderItem(line, item) item = QtWidgets.QTableWidgetItem() self.tabWidget.addTab(self.tab, "") self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName("CanOBD Cfg Set") self.mSpeedTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mSpeedTreeWidget.setGeometry(QtCore.QRect(10, 0, 1031, 101)) self.mSpeedTreeWidget.setObjectName("mSpeedTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mSpeedTreeWidget.headerItem().setFont(0, font) self.mSpeedTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mSpeedTreeWidget.headerItem().setFont(3, font) self.mRPMTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mRPMTreeWidget.setGeometry(QtCore.QRect(10, 100, 1031, 91)) self.mRPMTreeWidget.setObjectName("mRPMTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mRPMTreeWidget.headerItem().setFont(0, font) self.mRPMTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mRPMTreeWidget.headerItem().setFont(3, font) self.mVDHRTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mVDHRTreeWidget.setGeometry(QtCore.QRect(10, 190, 1031, 91)) self.mVDHRTreeWidget.setObjectName("mVDHRTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mVDHRTreeWidget.headerItem().setFont(0, font) self.mVDHRTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mVDHRTreeWidget.headerItem().setFont(3, font) self.mHoursTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mHoursTreeWidget.setGeometry(QtCore.QRect(10, 280, 1031, 101)) self.mHoursTreeWidget.setObjectName("mHoursTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mHoursTreeWidget.headerItem().setFont(0, font) self.mHoursTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mHoursTreeWidget.headerItem().setFont(3, font) self.mEECTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mEECTreeWidget.setGeometry(QtCore.QRect(10, 380, 1031, 91)) self.mEECTreeWidget.setObjectName("mEECTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mEECTreeWidget.headerItem().setFont(0, font) self.mEECTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mEECTreeWidget.headerItem().setFont(3, font) self.mET1TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mET1TreeWidget.setGeometry(QtCore.QRect(10, 470, 1031, 101)) self.mET1TreeWidget.setObjectName("mET1TreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mET1TreeWidget.headerItem().setFont(0, font) self.mET1TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mET1TreeWidget.headerItem().setFont(3, font) self.mAT1T1ITreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mAT1T1ITreeWidget.setGeometry(QtCore.QRect(10, 570, 1031, 91)) self.mAT1T1ITreeWidget.setObjectName("mAT1T1ITreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mAT1T1ITreeWidget.headerItem().setFont(0, font) self.mAT1T1ITreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mAT1T1ITreeWidget.headerItem().setFont(3, font) self.mLFETreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mLFETreeWidget.setGeometry(QtCore.QRect(10, 660, 1031, 101)) self.mLFETreeWidget.setObjectName("mLFETreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mLFETreeWidget.headerItem().setFont(0, font) self.mLFETreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mLFETreeWidget.headerItem().setFont(3, font) self.mETC2TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mETC2TreeWidget.setGeometry(QtCore.QRect(10, 760, 1031, 101)) self.mETC2TreeWidget.setObjectName("mETC2TreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mETC2TreeWidget.headerItem().setFont(0, font) self.mETC2TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mETC2TreeWidget.headerItem().setFont(3, font) self.tabWidget.addTab(self.tab_2, "") self.mComCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mComCfgBox.setGeometry(QtCore.QRect(1040, 10, 191, 231)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mComCfgBox.setFont(font) self.mComCfgBox.setObjectName("mComCfgBox") self.mPortName = QtWidgets.QLabel(self.mComCfgBox) self.mPortName.setGeometry(QtCore.QRect(20, 30, 61, 21)) self.mPortName.setObjectName("mPortName") self.mBpsName = QtWidgets.QLabel(self.mComCfgBox) self.mBpsName.setGeometry(QtCore.QRect(20, 60, 61, 21)) self.mBpsName.setObjectName("mBpsName") self.mDatabitName = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitName.setGeometry(QtCore.QRect(20, 90, 61, 21)) self.mDatabitName.setObjectName("mDatabitName") self.mStopName = QtWidgets.QLabel(self.mComCfgBox) self.mStopName.setGeometry(QtCore.QRect(20, 120, 61, 21)) self.mStopName.setObjectName("mStopName") self.mOddName = QtWidgets.QLabel(self.mComCfgBox) self.mOddName.setGeometry(QtCore.QRect(20, 150, 61, 21)) self.mOddName.setObjectName("mOddName") self.mDatabitVal = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitVal.setGeometry(QtCore.QRect(100, 90, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mDatabitVal.setFont(font) self.mDatabitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mDatabitVal.setAlignment(QtCore.Qt.AlignCenter) self.mDatabitVal.setObjectName("mDatabitVal") self.mStopBitVal = QtWidgets.QLabel(self.mComCfgBox) self.mStopBitVal.setGeometry(QtCore.QRect(100, 120, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mStopBitVal.setFont(font) self.mStopBitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mStopBitVal.setAlignment(QtCore.Qt.AlignCenter) self.mStopBitVal.setObjectName("mStopBitVal") self.mOddVal = QtWidgets.QLabel(self.mComCfgBox) self.mOddVal.setGeometry(QtCore.QRect(100, 150, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mOddVal.setFont(font) self.mOddVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mOddVal.setAlignment(QtCore.Qt.AlignCenter) self.mOddVal.setObjectName("mOddVal") self.mPortVal = QtWidgets.QComboBox(self.mComCfgBox) self.mPortVal.setGeometry(QtCore.QRect(90, 30, 81, 22)) self.mPortVal.setObjectName("mPortVal") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mBPSVal = QtWidgets.QComboBox(self.mComCfgBox) self.mBPSVal.setGeometry(QtCore.QRect(90, 60, 81, 22)) self.mBPSVal.setObjectName("mBPSVal") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mOpenSerial = QtWidgets.QDialogButtonBox(self.mComCfgBox) self.mOpenSerial.setGeometry(QtCore.QRect(20, 190, 156, 31)) self.mOpenSerial.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Open) self.mOpenSerial.setObjectName("mOpenSerial") self.mCycleCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mCycleCfgBox.setGeometry(QtCore.QRect(1040, 260, 191, 221)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mCycleCfgBox.setFont(font) self.mCycleCfgBox.setObjectName("mCycleCfgBox") self.mcheck1000ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck1000ms.setGeometry(QtCore.QRect(20, 180, 141, 31)) self.mcheck1000ms.setObjectName("mcheck1000ms") self.mcheck500ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck500ms.setGeometry(QtCore.QRect(20, 150, 141, 31)) self.mcheck500ms.setObjectName("mcheck500ms") self.mcheck100ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck100ms.setGeometry(QtCore.QRect(20, 90, 141, 31)) self.mcheck100ms.setObjectName("mcheck100ms") self.mcheck50ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck50ms.setGeometry(QtCore.QRect(20, 60, 141, 31)) self.mcheck50ms.setObjectName("mcheck50ms") self.mcheck20ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck20ms.setGeometry(QtCore.QRect(20, 30, 141, 31)) self.mcheck20ms.setObjectName("mcheck20ms") self.mcheck200ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck200ms.setGeometry(QtCore.QRect(20, 120, 141, 31)) self.mcheck200ms.setObjectName("mcheck200ms") self.mEventSigBox = QtWidgets.QGroupBox(self.centralwidget) self.mEventSigBox.setGeometry(QtCore.QRect(1050, 490, 191, 151)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mEventSigBox.setFont(font) self.mEventSigBox.setObjectName("mEventSigBox") self.radioLeftREvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioLeftREvent.setGeometry(QtCore.QRect(10, 30, 151, 16)) self.radioLeftREvent.setObjectName("radioLeftREvent") self.radioKiilEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioKiilEvent.setGeometry(QtCore.QRect(10, 90, 151, 16)) self.radioKiilEvent.setObjectName("radioKiilEvent") self.radioPEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioPEvent.setGeometry(QtCore.QRect(10, 120, 151, 16)) self.radioPEvent.setObjectName("radioPEvent") self.radioOpenCloseEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioOpenCloseEvent.setGeometry(QtCore.QRect(10, 60, 151, 16)) self.radioOpenCloseEvent.setObjectName("radioOpenCloseEvent") self.mReadOBDinfBox = QtWidgets.QGroupBox(self.centralwidget) self.mReadOBDinfBox.setGeometry(QtCore.QRect(1050, 660, 191, 171)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mReadOBDinfBox.setFont(font) self.mReadOBDinfBox.setObjectName("mReadOBDinfBox") self.radioVinRead = QtWidgets.QRadioButton(self.mReadOBDinfBox) self.radioVinRead.setGeometry(QtCore.QRect(10, 40, 141, 21)) self.radioVinRead.setObjectName("radioVinRead") self.mVinInfShow = QtWidgets.QTextBrowser(self.mReadOBDinfBox) self.mVinInfShow.setGeometry(QtCore.QRect(10, 70, 171, 91)) self.mVinInfShow.setObjectName("mVinInfShow") MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) for num in range(0, 150, 1): item = self.tableWidget.verticalHeaderItem(num) item.setText(_translate("MainWindow", str(num +1))) item = self.tableWidget.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "时间标识")) item = self.tableWidget.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "帧ID")) item = self.tableWidget.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "帧类型")) item = self.tableWidget.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "长度")) item = self.tableWidget.horizontalHeaderItem(4) item.setText(_translate("MainWindow", "数据 (BIT7--BIT0 大端模式)")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1")) self.mSpeedTreeWidget.headerItem().setText(0, _translate("MainWindow", "速度[CCVS1]")) self.mSpeedTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mSpeedTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mSpeedTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(km/h)")) self.mSpeedTreeWidget.setColumnWidth(0, 150) self.mSpeedTreeWidget.setColumnWidth(1, 150) self.mSpeedTreeWidget.setColumnWidth(2, 550) self.mSpeedTreeWidget.setColumnWidth(3, 150) self.mRPMTreeWidget.headerItem().setText(0, _translate("MainWindow", "转速[EEC1]")) self.mRPMTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mRPMTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mRPMTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(rpm)")) self.mRPMTreeWidget.setColumnWidth(0, 150) self.mRPMTreeWidget.setColumnWidth(1, 150) self.mRPMTreeWidget.setColumnWidth(2, 550) self.mRPMTreeWidget.setColumnWidth(3, 150) self.mVDHRTreeWidget.headerItem().setText(0, _translate("MainWindow", "里程[VDHR]")) self.mVDHRTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mVDHRTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mVDHRTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(km)")) self.mVDHRTreeWidget.setColumnWidth(0, 150) self.mVDHRTreeWidget.setColumnWidth(1, 150) self.mVDHRTreeWidget.setColumnWidth(2, 550) self.mVDHRTreeWidget.setColumnWidth(3, 150) self.mHoursTreeWidget.headerItem().setText(0, _translate("MainWindow", "工作时长[HOURS]")) self.mHoursTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mHoursTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mHoursTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(hours)")) self.mHoursTreeWidget.setColumnWidth(0, 150) self.mHoursTreeWidget.setColumnWidth(1, 150) self.mHoursTreeWidget.setColumnWidth(2, 550) self.mHoursTreeWidget.setColumnWidth(3, 150) self.mEECTreeWidget.headerItem().setText(0, _translate("MainWindow", "发动机负载[EEC1]")) self.mEECTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mEECTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mEECTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(%)")) self.mEECTreeWidget.setColumnWidth(0, 150) self.mEECTreeWidget.setColumnWidth(1, 150) self.mEECTreeWidget.setColumnWidth(2, 550) self.mEECTreeWidget.setColumnWidth(3, 150) self.mET1TreeWidget.headerItem().setText(0, _translate("MainWindow", "冷却液温度[ET1]")) self.mET1TreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mET1TreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mET1TreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(°)")) self.mET1TreeWidget.setColumnWidth(0, 150) self.mET1TreeWidget.setColumnWidth(1, 150) self.mET1TreeWidget.setColumnWidth(2, 550) self.mET1TreeWidget.setColumnWidth(3, 150) self.mAT1T1ITreeWidget.headerItem().setText(0, _translate("MainWindow", "燃油液面[AT1T1I]")) self.mAT1T1ITreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mAT1T1ITreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mAT1T1ITreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(%)")) self.mAT1T1ITreeWidget.setColumnWidth(0, 150) self.mAT1T1ITreeWidget.setColumnWidth(1, 150) self.mAT1T1ITreeWidget.setColumnWidth(2, 550) self.mAT1T1ITreeWidget.setColumnWidth(3, 150) self.mLFETreeWidget.headerItem().setText(0, _translate("MainWindow", "平均油耗[LFE]")) self.mLFETreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mLFETreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mLFETreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(L/h)")) self.mLFETreeWidget.setColumnWidth(0, 150) self.mLFETreeWidget.setColumnWidth(1, 150) self.mLFETreeWidget.setColumnWidth(2, 550) self.mLFETreeWidget.setColumnWidth(3, 150) self.mETC2TreeWidget.headerItem().setText(0, _translate("MainWindow", "档位[ETC2]")) self.mETC2TreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mETC2TreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mETC2TreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal")) self.mETC2TreeWidget.setColumnWidth(0, 150) self.mETC2TreeWidget.setColumnWidth(1, 150) self.mETC2TreeWidget.setColumnWidth(2, 550) self.mETC2TreeWidget.setColumnWidth(3, 150) self.tableWidget.setColumnWidth(0, 200) self.tableWidget.setColumnWidth(1, 150) self.tableWidget.setColumnWidth(4,450) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "CanOBD Inf Show")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "CanOBD J1939 Show")) self.mComCfgBox.setTitle(_translate("MainWindow", "串口配置")) self.mPortName.setText(_translate("MainWindow", "端口号")) self.mBpsName.setText(_translate("MainWindow", "波特率")) self.mDatabitName.setText(_translate("MainWindow", "数据位")) self.mStopName.setText(_translate("MainWindow", "停止位")) self.mOddName.setText(_translate("MainWindow", "检验位")) self.mDatabitVal.setText(_translate("MainWindow", "8")) self.mStopBitVal.setText(_translate("MainWindow", "1")) self.mOddVal.setText(_translate("MainWindow", "无")) self.mBPSVal.setItemText(0, _translate("MainWindow", "9600")) self.mBPSVal.setItemText(1, _translate("MainWindow", "19200")) self.mBPSVal.setItemText(2, _translate("MainWindow", "115200")) self.mBPSVal.setItemText(3, _translate("MainWindow", "230400")) self.mBPSVal.setItemText(4, _translate("MainWindow", "256000")) self.mBPSVal.setItemText(5, _translate("MainWindow", "460800")) port_list = list(serial.tools.list_ports.comports()) if port_list.__len__() is not 0: for num in range(port_list.__len__()): self.mPortVal.setItemText(num, _translate("MainWindow", str(port_list[num].device))) serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial = SerialThread(serialport, serialbaudrate) self.mCycleCfgBox.setTitle(_translate("MainWindow", "过滤设置(周期)")) self.mcheck1000ms.setText(_translate("MainWindow", "1000ms 周期")) self.mcheck500ms.setText(_translate("MainWindow", "500ms 周期")) self.mcheck100ms.setText(_translate("MainWindow", "100ms 周期")) self.mcheck50ms.setText(_translate("MainWindow", "50ms 周期")) self.mcheck20ms.setText(_translate("MainWindow", "20ms 周期")) self.mcheck200ms.setText(_translate("MainWindow", "200ms 周期")) self.mEventSigBox.setTitle(_translate("MainWindow", "事件信号策略")) self.radioLeftREvent.setText(_translate("MainWindow", "左右转 事件")) self.radioKiilEvent.setText(_translate("MainWindow", "刹车 事件")) self.radioPEvent.setText(_translate("MainWindow", "档位 事件")) self.radioOpenCloseEvent.setText(_translate("MainWindow", "开关门 事件")) self.mReadOBDinfBox.setTitle(_translate("MainWindow", "主动读取信息")) self.radioVinRead.setText(_translate("MainWindow", "VIN 信息")) def OpenSerial(self): if self.LSerial != None: if self.LSerial.SerialIsOpen(): self.LSerial.__del__() port_list = list(serial.tools.list_ports.comports()) if port_list.__len__() != 0: serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial.__init__(serialport,serialbaudrate) # 开启线程 self.thread = Worker() # 创建线程对象 self.thread.update_signal.connect(self.CanOBDdatarefresh) # 连接信号和槽 self.thread.update_signal.connect(self.CanOBDSignalAnalyPro) # 连接信号和槽 # self.thread.update_signal.connect(self.LSerial.Com_read_frame) # 连接信号和槽 self.thread.start() # 启动线程 #self.LSerial.Com_read_frame() self.LSerial.start_reading() # <-- 在这里启动读取线程 def CloseSerial(self): if self.LSerial.SerialIsOpen(): self.LSerial.close() def Serialconnectslot(self): self.mOpenSerial.accepted.connect(self.OpenSerial) self.mOpenSerial.rejected.connect(self.CloseSerial) def get_checked_cycles(self): """返回用户勾选的所有周期值(毫秒)""" checked_cycles = [] if self.mcheck20ms.isChecked(): checked_cycles.append(10) if self.mcheck50ms.isChecked(): checked_cycles.append(50) if self.mcheck100ms.isChecked(): checked_cycles.append(100) if self.mcheck200ms.isChecked(): checked_cycles.append(200) if self.mcheck500ms.isChecked(): checked_cycles.append(500) if self.mcheck1000ms.isChecked(): checked_cycles.append(1000) return checked_cycles def CanOBDdatarefresh(self): # # 检查数据接收是否超时(1秒阈值) # current_time = time.time() # if current_time - self.LSerial.last_data_time > 1.0: # # 清空缓冲区并跳过刷新 # global frame_buffer # frame_buffer = bytearray() # return filtered_cycles = self.get_checked_cycles() all_update_ids = self.LSerial.data_updated_ids | self.LSerial.new_added_ids for can_id in all_update_ids: # 查找该ID在列表中的位置 row_index = None for idx, item in enumerate(CanOBDItemList): if item[1] == can_id: row_index = idx break if row_index is None or row_index >= self.tableWidget.rowCount(): continue # 周期过滤检查 if can_id in self.LSerial.cycle_dict: cycle = self.LSerial.cycle_dict[can_id] skip = False for filtered_cycle in filtered_cycles: tolerance = filtered_cycle * 0.1 if abs(cycle - filtered_cycle) <= tolerance: skip = True break if skip: continue # 更新表格行 item_data = CanOBDItemList[row_index] self.tableWidget.setItem(row_index, 0, QtWidgets.QTableWidgetItem(str(item_data[0]))) self.tableWidget.setItem(row_index, 1, QtWidgets.QTableWidgetItem(str(item_data[1]))) self.tableWidget.setItem(row_index, 2, QtWidgets.QTableWidgetItem(str(item_data[2]))) self.tableWidget.setItem(row_index, 3, QtWidgets.QTableWidgetItem(str(item_data[3]))) self.tableWidget.setItem(row_index, 4, QtWidgets.QTableWidgetItem(str(item_data[4]))) # 清空标志位 self.LSerial.data_updated_ids.clear() self.LSerial.new_added_ids.clear() self.tableWidget.show() def CanOBDSignalAnalyPro(self): index = 0 bfindflag = 0 if all(not sublist for sublist in CanPGNItemList) or CanPGNItemList[0][0] == 0: if len(CanPGNItemList): CanPGNItemList.pop(0) else: for signalindex in CanPGNItemList: value = ''.join(c for c in signalindex[0].lower() if c in '0123456789abcdef') # 判断是否需要补零(前面补) if len(value) % 2 != 0: value = '0' + value # 将处理后的字符串重新赋值给 signalindex[0] signalindex[0] = value PGNCanID = bytes.fromhex(str(signalindex[0])).hex() #PGNCanID = int(signalindex[0]) #车速 if (PGNCanID == bytes.fromhex("FEF1").hex()): num_top_items = self.mSpeedTreeWidget.topLevelItemCount() for index in range(num_top_items ): if (self.mSpeedTreeWidget.topLevelItem(index).text(1) == str(signalindex[1])): self.mSpeedTreeWidget.topLevelItem(index).setText(0, str(signalindex[0])) self.mSpeedTreeWidget.topLevelItem(index).setText(1, str(signalindex[1])) self.mSpeedTreeWidget.topLevelItem(index).setText(2, str(signalindex[2])) self.mSpeedTreeWidget.topLevelItem(index).setText(3, str(signalindex[3])) bfindflag = 1 break #self.mSpeedTreeWidget.addTopLevelItem(speeditem) if bfindflag == 0: speeditem = QTreeWidgetItem(self.mSpeedTreeWidget) speeditem.setText(0, str(signalindex[0])) speeditem.setText(1, str(signalindex[1])) speeditem.setText(2, str(signalindex[2])) speeditem.setText(3, str(signalindex[3])) self.mSpeedTreeWidget.addTopLevelItem(speeditem) self.mSpeedTreeWidget.expandAll() self.mSpeedTreeWidget.show() class Worker(QThread): update_signal = pyqtSignal(int) # 定义一个信号,用于传递更新信息到主线程 def run(self): # 模拟耗时操作 while True: time.sleep(0.1) self.update_signal.emit(1) # 发射信号,传递更新信息 这是canobd文件 import serial import time import struct import queue import threading from datetime import datetime CanOBDItemList = [[0,0,0,0,0]] CanPGNItemList = [[0,0,0,0]] Frame_start = b’\xFF’ Frame_end = b’\x55’ Frame_data_style_len = 6 Frame_Data_Len = 0 frame_buffer = bytearray() class CanInfShow_Item: def int(self,CanID,CanFramType,Len,CanDataInf): self.SystemCycle = datetime.strftime(“%Y-%m-%d %H:%M:%S.%f”)[:-3], self.CanID = CanID, self.CanFrame = CanFramType, self.CanDataLen = Len, self.CanData = CanDataInf class CanPGNShow_Item: def int(self, PGNID, CanID, CanData, Signal): self.PGNID = PGNID, self.CanID = CanID, self.CanData = CanData, self.Signal = Signal class SerialPort: def init(self, port, baudrate): # 初始化串口参数 self.port = port self.baudrate = baudrate self.ser = serial.Serial( port=self.port, baudrate=self.baudrate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE ) # 等待串口连接稳定 self.last_data_time = time.time() # 新增:最后接收数据的时间戳 self.cycle_dict = {} # 存储{帧ID: [上次时间戳, 当前周期]} self.last_frame_time = {} # 存储每个ID的最后出现时间 self.data_updated_ids = set() # 存储数据变化的CAN ID self.new_added_ids = set() # 存储新增的CAN ID self.state = 0 self.current_frame = bytearray() self.expected_length = 0 self.raw_data_queue = queue.Queue(maxsize=10000) self.data_lock = threading.Lock() self.worker_threads = [] time.sleep(0.2) if not self.ser.isOpen(): print("串口打开失败!") def close(self): # 关闭串口连接 if self.ser.isOpen(): self.ser.close() def send(self, data): # 发送数据 if self.ser.isOpen(): try: self.ser.write(data.encode('utf-8')) except serial.SerialException as e: print(f"发送数据失败: {e}") # def recv(self): # # 接收数据 # if self.ser.isOpen(): # data = self.ser.read(self.ser.inWaiting()) # if data: # 有数据时更新时间戳 # self.last_data_time = time.time() # return data def recv(self, chunk_size=1024): if self.ser.isOpen(): # 每次最多读取1024字节 data = self.ser.read(min(self.ser.inWaiting(), chunk_size)) if data: self.last_data_time = time.time() return data return None def __del__(self): self.close() def SerialIsOpen(self): if self.ser.isOpen(): return 1 else: return 0 def start_reading(self): self.recv_thread = threading.Thread(target=self._recv_worker, daemon=True) self.parse_thread = threading.Thread(target=self._parse_worker, daemon=True) self.recv_thread.start() self.parse_thread.start() self.worker_threads = [self.recv_thread, self.parse_thread] def _recv_worker(self): while self.ser.isOpen(): data = self.recv(chunk_size=1024) # 每次最多读1KB if data: self.raw_data_queue.put(data) else: time.sleep(0.001) def _parse_worker(self): while True: try: data = self.raw_data_queue.get(timeout=0.1) for byte in data: self.process_byte(byte) except queue.Empty: continue def process_byte(self, byte): """ 使用状态机逐字节解析帧结构。 """ if self.state == 0: # 等待帧头 FF if byte == 0xFF: self.current_frame.append(byte) self.state = 1 else: # 如果不是帧头,忽略该字节 pass elif self.state == 1: # 等待帧头 55 if byte == 0x55: self.current_frame.append(byte) self.state = 2 else: # 如果第二字节不是55,重置 self.reset_state() elif self.state == 2: # 接收总长度高位 (第2字节) self.current_frame.append(byte) self.state = 3 elif self.state == 3: # 接收总长度低位 (第3字节) self.current_frame.append(byte) # 计算总长度(从第2字节开始) length_high = self.current_frame[2] length_low = byte self.expected_length = (length_high << 8) | length_low self.state = 4 elif self.state == 4: # 接收类型字段 (第4字节) self.current_frame.append(byte) self.state = 5 elif self.state == 5: # 接收保留字段 (第5字节) self.current_frame.append(byte) self.state = 6 elif self.state == 6: # 接收 CAN 通道类型 (第6字节) self.current_frame.append(byte) self.state = 7 elif self.state == 7: # 接收 CAN 报文个数 N (第7字节) self.current_frame.append(byte) self.num_messages = byte self.state = 8 self.messages_received = 0 elif self.state == 8: # 接收 CAN 报文数据 (每条12字节) self.current_frame.append(byte) self.messages_received += 1 if self.messages_received >= self.num_messages * 12: # 所有报文接收完成,准备校验位 self.state = 9 elif self.state == 9: # 接收校验位 self.current_frame.append(byte) self.state = 10 elif self.state == 10: # 完整帧已接收,验证校验和 if self.verify_checksum(): # 直接处理帧数据(关键修改点) self.Frame_analoy_process(bytes(self.current_frame)) else: print("校验失败,丢弃当前帧") self.reset_state() def verify_checksum(self): """ 验证校验和:从第2字节到倒数第二个字节之和 & 0xFF """ data_to_check = self.current_frame[2:-1] # 从第2字节到最后一个校验位之前 checksum = sum(data_to_check) & 0xFF return checksum == self.current_frame[-1] def reset_state(self): """ 重置状态机 """ self.state = 0 self.current_frame = bytearray() self.expected_length = 0 self.messages_received = 0 #报文解析 def Frame_analoy_process(self, Framedata): # 检查帧类型 (0x0C 0x98) if len(Framedata) < 8 or Framedata[4] != 0x0C or Framedata[5] != 0x98: return try: FrameNum = int(Framedata[7]) except IndexError: return # 检查是否有足够数据 if len(Framedata) < 12 * FrameNum + 8: return current_time = time.time() # 获取当前精确时间戳 for index in range(0,FrameNum): # 时间戳 Cantime = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] try: id_bytes = [ Framedata[12 * index + 11], # LSB Framedata[12 * index + 10], Framedata[12 * index + 9], Framedata[12 * index + 8] # MSB ] except IndexError: continue # 格式化为8位大写十六进制 CanID = ''.join(format(b, '02X') for b in id_bytes) # 提取ID字节 CanFramType = "Cycle" Len = 8 CanDataSpace = '' PGNdata = '' PGNID = int(Framedata[12 * index + 9] ) + int(Framedata[12 * index + 10]* 0x100) PGNSignl = self.Frame_analoy_PGN_Signal(PGNID,Framedata[12 * index + 12:]) # 提取数据部分 for posindex in range(0,8): PGNdata += str(hex(Framedata[12 * index + 12 + posindex])[2:]) try: CanDataSpace = ' '.join( format(Framedata[12 * index + 12 + posindex], '02X') for posindex in range(8) ) except IndexError: continue CanItemData = [Cantime, CanID, CanFramType, Len, CanDataSpace] if PGNSignl != None: SignalItemData = [hex(PGNID)[2:], CanID, PGNdata, PGNSignl] if all(not sublist for sublist in CanPGNItemList) or CanPGNItemList[0][0] == 0: if len(CanPGNItemList): CanPGNItemList.pop(0) CanPGNItemList.insert(0, SignalItemData) else: Listpos = self.find_in_2d_list(CanPGNItemList, CanID) if None != Listpos: CanPGNItemList[Listpos[0]] = SignalItemData else: CanPGNItemList.append(SignalItemData) if CanID in self.last_frame_time: last_time = self.last_frame_time[CanID] cycle = (current_time - last_time) * 1000 # 转换为毫秒 # 平滑处理:使用加权平均减少抖动 if CanID in self.cycle_dict: old_cycle = self.cycle_dict[CanID] self.cycle_dict[CanID] = 0.7 * old_cycle + 0.3 * cycle else: self.cycle_dict[CanID] = cycle else: self.cycle_dict[CanID] = 0 # 首次出现,周期设为0 self.last_frame_time[CanID] = current_time # 更新列表 if not CanOBDItemList or CanOBDItemList[0][0] == 0: if CanOBDItemList and CanOBDItemList[0][0] == 0: CanOBDItemList.pop(0) CanOBDItemList.insert(0, CanItemData) self.new_added_ids.add(CanID) # 新增标志 else: Listpos = self.find_in_2d_list(CanOBDItemList, CanID) if Listpos is not None: CanOBDItemList[Listpos[0]] = CanItemData self.data_updated_ids.add(CanID) # 更新标志 else: CanOBDItemList.append(CanItemData) self.new_added_ids.add(CanID) # 新增标志 self.last_data_time = time.time() # 解析到有效帧时更新时间 def find_in_2d_list(self,matrix, target): for i, row in enumerate(matrix): if any(x == target for x in row): return (i, row.index(target)) # 使用row.index()找到具体列的索引 return None def Frame_analoy_PGN_Signal(self,PGNID, Framedata): #车速 SignalVal = 0 if PGNID == 0xFEF1: SignalVal = float(int(Framedata[2] * 0x100) + int(Framedata[3]) / 256.0) elif PGNID == 0xF004: SignalVal = str(int(Framedata[2] & 0x7F)) + '|'+ str(float(int(Framedata[4] * 0x100) + int(Framedata[3]) / 125.0)) elif PGNID == 0xFCE1: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEE5: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEE5: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEEE: SignalVal = int(Framedata[0] - 40) elif PGNID == 0xFE56: SignalVal = float(Framedata[0] * 0.4) elif PGNID == 0xFEF2: SignalVal = float(int(Framedata[1] * 0x100 + Framedata[0] * 0.05)) elif PGNID == 0xF005: SignalVal = Framedata[3] return SignalVal # def start_reading(self): # self.read_thread = threading.Thread(target=self.Com_read_frame, daemon=True) # self.read_thread.start() 这是serialpro文件 import sys sys.path.append(‘/UI/Login’) from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QApplication, QWidget #from PyQt5.QtCore import QThread, pyqtSignal,QTimer from Login import Ui_Form as Login_ui from CanOBD import Ui_MainWindow as CanOBD_ui from PyQt5.QtWidgets import QMessageBox import time #import threading class LogInViewUI(QtWidgets.QWidget,Login_ui): def int(self): super(LogInViewUI,self).int() self.setupUi(self) def connectslot(self): #self.m_loginbuttonOn.clicked.connect(self.login_button) self.mloginButton.clicked.connect(self.login_button) def login_button(self): if self.mPassWordEdit.acceptRichText() == "": QMessageBox.warning(self, '警告', '密码不能为空,请输入!') return None # 1打开新窗口 CanOBDWidget.show() # 2关闭本窗口 LoginWidget.close() class CanOBDViewUI(QtWidgets.QWidget,CanOBD_ui): def int(self): super(CanOBDViewUI,self).int() self.setupUi(self) if name == “main”: app = QtWidgets.QApplication(sys.argv) #MainWidget = QtWidgets.QDockWidget() LoginWidget = QtWidgets.QWidget() CanOBDWidget = QtWidgets.QMainWindow() loginui = LogInViewUI() CanOBDui = CanOBDViewUI() loginui.setupUi(LoginWidget) loginui.connectslot() CanOBDui.setupUi(CanOBDWidget) CanOBDui.Serialconnectslot() LoginWidget.show() sys.exit(app.exec_()) 这是main文件 这个程序在115200波特率的情况下读取can数据(每秒1200条)的过程中,存在一定跳帧读取现象,30帧会跳过10帧,应该怎么优化?
最新发布
07-16
import sys sys.path.append('/UI/Login') from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QApplication, QWidget #from PyQt5.QtCore import QThread, pyqtSignal,QTimer from Login import Ui_Form as Login_ui from CanOBD import Ui_MainWindow as CanOBD_ui from PyQt5.QtWidgets import QMessageBox import time #import threading class LogInViewUI(QtWidgets.QWidget,Login_ui): def __int__(self): super(LogInViewUI,self).__int__() self.setupUi(self) def connectslot(self): #self.m_loginbuttonOn.clicked.connect(self.login_button) self.mloginButton.clicked.connect(self.login_button) def login_button(self): if self.mPassWordEdit.acceptRichText() == "": QMessageBox.warning(self, '警告', '密码不能为空,请输入!') return None # 1打开新窗口 CanOBDWidget.show() # 2关闭本窗口 LoginWidget.close() class CanOBDViewUI(QtWidgets.QWidget,CanOBD_ui): def __int__(self): super(CanOBDViewUI,self).__int__() self.setupUi(self) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) #MainWidget = QtWidgets.QDockWidget() LoginWidget = QtWidgets.QWidget() CanOBDWidget = QtWidgets.QMainWindow() loginui = LogInViewUI() CanOBDui = CanOBDViewUI() loginui.setupUi(LoginWidget) loginui.connectslot() CanOBDui.setupUi(CanOBDWidget) CanOBDui.Serialconnectslot() LoginWidget.show() sys.exit(app.exec_()) 这是main文件 import serial import time import struct import queue import threading from datetime import datetime can_obd_lock = threading.Lock() can_pgn_lock = threading.Lock() CanOBDItemList = [[0,0,0,0,0]] CanPGNItemList = [[0,0,0,0]] Frame_start = b'\xFF' Frame_end = b'\x55' Frame_data_style_len = 6 Frame_Data_Len = 0 frame_buffer = bytearray() class CanInfShow_Item: def __int__(self,CanID,CanFramType,Len,CanDataInf): self.SystemCycle = datetime.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3], self.CanID = CanID, self.CanFrame = CanFramType, self.CanDataLen = Len, self.CanData = CanDataInf class CanPGNShow_Item: def __int__(self, PGNID, CanID, CanData, Signal): self.PGNID = PGNID, self.CanID = CanID, self.CanData = CanData, self.Signal = Signal class SerialPort: def __init__(self, port, baudrate): # 初始化串口参数 self.port = port self.baudrate = baudrate self.ser = serial.Serial( port=self.port, baudrate=self.baudrate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE ) # 等待串口连接稳定 self.last_data_time = time.time() # 新增:最后接收数据的时间戳 self.cycle_dict = {} # 存储{帧ID: [上次时间戳, 当前周期]} self.last_frame_time = {} # 存储每个ID的最后出现时间 self.data_updated_ids = set() # 存储数据变化的CAN ID self.new_added_ids = set() # 存储新增的CAN ID self.state = 0 self.current_frame = bytearray() self.expected_length = 0 self.raw_data_queue = queue.Queue(maxsize=10000) self.data_lock = threading.Lock() self.worker_threads = [] time.sleep(0.2) if not self.ser.isOpen(): print("串口打开失败!") def close(self): # 关闭串口连接 if self.ser.isOpen(): self.ser.close() def send(self, data): # 发送数据 if self.ser.isOpen(): try: self.ser.write(data.encode('utf-8')) except serial.SerialException as e: print(f"发送数据失败: {e}") # def recv(self): # # 接收数据 # if self.ser.isOpen(): # data = self.ser.read(self.ser.inWaiting()) # if data: # 有数据时更新时间戳 # self.last_data_time = time.time() # return data def recv(self, chunk_size=1024): if self.ser.isOpen(): # 每次最多读取1024字节 data = self.ser.read(min(self.ser.inWaiting(), chunk_size)) if data: self.last_data_time = time.time() return data return None def __del__(self): self.close() def SerialIsOpen(self): if self.ser.isOpen(): return 1 else: return 0 def start_reading(self): self.recv_thread = threading.Thread(target=self._recv_worker, daemon=True) self.parse_thread = threading.Thread(target=self._parse_worker, daemon=True) self.recv_thread.start() self.parse_thread.start() self.worker_threads = [self.recv_thread, self.parse_thread] def _recv_worker(self): while self.ser.isOpen(): data = self.recv(chunk_size=4096) # 每次最多读4KB if data: self.raw_data_queue.put(data) else: time.sleep(0.001) def _parse_worker(self): while True: try: data = self.raw_data_queue.get(timeout=0.1) for byte in data: self.process_byte(byte) except queue.Empty: continue def process_byte(self, byte): """ 使用状态机逐字节解析帧结构。 """ if self.state == 0: # 等待帧头 FF if byte == 0xFF: self.current_frame.append(byte) self.state = 1 else: # 如果不是帧头,忽略该字节 pass elif self.state == 1: # 等待帧头 55 if byte == 0x55: self.current_frame.append(byte) self.state = 2 else: # 如果第二字节不是55,重置 self.reset_state() elif self.state == 2: # 接收总长度高位 (第2字节) self.current_frame.append(byte) self.state = 3 elif self.state == 3: # 接收总长度低位 (第3字节) self.current_frame.append(byte) # 计算总长度(从第2字节开始) length_high = self.current_frame[2] length_low = byte self.expected_length = (length_high << 8) | length_low self.state = 4 elif self.state == 4: # 接收类型字段 (第4字节) self.current_frame.append(byte) self.state = 5 elif self.state == 5: # 接收保留字段 (第5字节) self.current_frame.append(byte) self.state = 6 elif self.state == 6: # 接收 CAN 通道类型 (第6字节) self.current_frame.append(byte) self.state = 7 elif self.state == 7: # 接收 CAN 报文个数 N (第7字节) self.current_frame.append(byte) self.num_messages = byte self.state = 8 self.messages_received = 0 elif self.state == 8: # 接收 CAN 报文数据 (每条12字节) self.current_frame.append(byte) self.messages_received += 1 if self.messages_received >= self.num_messages * 12: # 所有报文接收完成,准备校验位 self.state = 9 elif self.state == 9: # 接收校验位 self.current_frame.append(byte) self.state = 10 elif self.state == 10: # 完整帧已接收,验证校验和 if self.verify_checksum(): # 直接处理帧数据(关键修改点) self.Frame_analoy_process(bytes(self.current_frame)) else: print("校验失败,丢弃当前帧") self.reset_state() def verify_checksum(self): """ 验证校验和:从第2字节到倒数第二个字节之和 & 0xFF """ data_to_check = self.current_frame[2:-1] # 从第2字节到最后一个校验位之前 checksum = sum(data_to_check) & 0xFF return checksum == self.current_frame[-1] def reset_state(self): """ 重置状态机 """ self.state = 0 self.current_frame = bytearray() self.expected_length = 0 self.messages_received = 0 #报文解析 def Frame_analoy_process(self, Framedata): # 检查帧类型 (0x0C 0x98) if len(Framedata) < 8 or Framedata[4] != 0x0C or Framedata[5] != 0x98: return try: FrameNum = int(Framedata[7]) except IndexError: return # 检查是否有足够数据 if len(Framedata) < 12 * FrameNum + 8: return current_time = time.time() # 获取当前精确时间戳 for index in range(0,FrameNum): # 时间戳 Cantime = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] try: id_bytes = [ Framedata[12 * index + 11], # LSB Framedata[12 * index + 10], Framedata[12 * index + 9], Framedata[12 * index + 8] # MSB ] except IndexError: continue # 格式化为8位大写十六进制 CanID = ''.join(format(b, '02X') for b in id_bytes) # 提取ID字节 CanFramType = "Cycle" Len = 8 CanDataSpace = '' PGNdata = '' PGNID = int(Framedata[12 * index + 9] ) + int(Framedata[12 * index + 10]* 0x100) PGNSignl = self.Frame_analoy_PGN_Signal(PGNID,Framedata[12 * index + 12:]) # 提取数据部分 for posindex in range(0,8): PGNdata += str(hex(Framedata[12 * index + 12 + posindex])[2:]) try: CanDataSpace = ' '.join( format(Framedata[12 * index + 12 + posindex], '02X') for posindex in range(8) ) except IndexError: continue CanItemData = [Cantime, CanID, CanFramType, Len, CanDataSpace] with can_pgn_lock: if PGNSignl != None: SignalItemData = [hex(PGNID)[2:], CanID, PGNdata, PGNSignl] if all(not sublist for sublist in CanPGNItemList) or CanPGNItemList[0][0] == 0: if len(CanPGNItemList): CanPGNItemList.pop(0) CanPGNItemList.insert(0, SignalItemData) else: Listpos = self.find_in_2d_list(CanPGNItemList, CanID) if None != Listpos: CanPGNItemList[Listpos[0]] = SignalItemData else: CanPGNItemList.append(SignalItemData) if CanID in self.last_frame_time: last_time = self.last_frame_time[CanID] cycle = (current_time - last_time) * 1000 # 转换为毫秒 # 平滑处理:使用加权平均减少抖动 if CanID in self.cycle_dict: old_cycle = self.cycle_dict[CanID] self.cycle_dict[CanID] = 0.7 * old_cycle + 0.3 * cycle else: self.cycle_dict[CanID] = cycle else: self.cycle_dict[CanID] = 0 # 首次出现,周期设为0 self.last_frame_time[CanID] = current_time # 更新列表 with can_obd_lock: if not CanOBDItemList or CanOBDItemList[0][0] == 0: if CanOBDItemList and CanOBDItemList[0][0] == 0: CanOBDItemList.pop(0) CanOBDItemList.insert(0, CanItemData) self.new_added_ids.add(CanID) # 新增标志 else: Listpos = self.find_in_2d_list(CanOBDItemList, CanID) if Listpos is not None: CanOBDItemList[Listpos[0]] = CanItemData self.data_updated_ids.add(CanID) # 更新标志 else: CanOBDItemList.append(CanItemData) self.new_added_ids.add(CanID) # 新增标志 self.last_data_time = time.time() # 解析到有效帧时更新时间 def find_in_2d_list(self,matrix, target): for i, row in enumerate(matrix): if any(x == target for x in row): return (i, row.index(target)) # 使用row.index()找到具体列的索引 return None def Frame_analoy_PGN_Signal(self,PGNID, Framedata): #车速 SignalVal = 0 if PGNID == 0xFEF1: SignalVal = float(int(Framedata[2] * 0x100) + int(Framedata[3]) / 256.0) elif PGNID == 0xF004: SignalVal = str(int(Framedata[2] & 0x7F)) + '|'+ str(float(int(Framedata[4] * 0x100) + int(Framedata[3]) / 125.0)) elif PGNID == 0xFCE1: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEE5: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEE5: SignalVal = int(Framedata[3] * 0x1000000) + int(Framedata[2] * 0x10000) + int(Framedata[1] * 0x100) + int(Framedata[0]) elif PGNID == 0xFEEE: SignalVal = int(Framedata[0] - 40) elif PGNID == 0xFE56: SignalVal = float(Framedata[0] * 0.4) elif PGNID == 0xFEF2: SignalVal = float(int(Framedata[1] * 0x100 + Framedata[0] * 0.05)) elif PGNID == 0xF005: SignalVal = Framedata[3] return SignalVal # def start_reading(self): # self.read_thread = threading.Thread(target=self.Com_read_frame, daemon=True) # self.read_thread.start() 这是serialpro文件 # -*- coding: utf-8 -*- import threading # Form implementation generated from reading ui file 'CanOBD.ui' # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QThread, pyqtSignal,QTimer from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem,QTreeWidget, QTreeWidgetItem from SerialPro import SerialPort as SerialThread from SerialPro import CanOBDItemList,CanPGNItemList from SerialPro import can_obd_lock, can_pgn_lock import time import serial import serial.tools.list_ports import binascii import struct class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1220, 940) font = QtGui.QFont() font.setPointSize(12) MainWindow.setFont(font) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setGeometry(QtCore.QRect(0, 0, 1041, 940)) self.tabWidget.setObjectName("tabWidget") self.tab = QtWidgets.QWidget() self.tab.setObjectName("tab") self.tableWidget = QtWidgets.QTableWidget(self.tab) self.tableWidget.setGeometry(QtCore.QRect(0, 0, 1031, 940)) self.tableWidget.setObjectName("tableWidget") self.tableWidget.setColumnCount(5) self.tableWidget.setRowCount(150) for num in range(0,150,1): item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(num, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() font.setKerning(False) item.setFont(font) for line in range(0,5,1): self.tableWidget.setHorizontalHeaderItem(line, item) item = QtWidgets.QTableWidgetItem() self.tabWidget.addTab(self.tab, "") self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName("CanOBD Cfg Set") self.mSpeedTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mSpeedTreeWidget.setGeometry(QtCore.QRect(10, 0, 1031, 101)) self.mSpeedTreeWidget.setObjectName("mSpeedTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mSpeedTreeWidget.headerItem().setFont(0, font) self.mSpeedTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mSpeedTreeWidget.headerItem().setFont(3, font) self.mRPMTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mRPMTreeWidget.setGeometry(QtCore.QRect(10, 100, 1031, 91)) self.mRPMTreeWidget.setObjectName("mRPMTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mRPMTreeWidget.headerItem().setFont(0, font) self.mRPMTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mRPMTreeWidget.headerItem().setFont(3, font) self.mVDHRTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mVDHRTreeWidget.setGeometry(QtCore.QRect(10, 190, 1031, 91)) self.mVDHRTreeWidget.setObjectName("mVDHRTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mVDHRTreeWidget.headerItem().setFont(0, font) self.mVDHRTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mVDHRTreeWidget.headerItem().setFont(3, font) self.mHoursTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mHoursTreeWidget.setGeometry(QtCore.QRect(10, 280, 1031, 101)) self.mHoursTreeWidget.setObjectName("mHoursTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mHoursTreeWidget.headerItem().setFont(0, font) self.mHoursTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mHoursTreeWidget.headerItem().setFont(3, font) self.mEECTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mEECTreeWidget.setGeometry(QtCore.QRect(10, 380, 1031, 91)) self.mEECTreeWidget.setObjectName("mEECTreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mEECTreeWidget.headerItem().setFont(0, font) self.mEECTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mEECTreeWidget.headerItem().setFont(3, font) self.mET1TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mET1TreeWidget.setGeometry(QtCore.QRect(10, 470, 1031, 101)) self.mET1TreeWidget.setObjectName("mET1TreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mET1TreeWidget.headerItem().setFont(0, font) self.mET1TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mET1TreeWidget.headerItem().setFont(3, font) self.mAT1T1ITreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mAT1T1ITreeWidget.setGeometry(QtCore.QRect(10, 570, 1031, 91)) self.mAT1T1ITreeWidget.setObjectName("mAT1T1ITreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mAT1T1ITreeWidget.headerItem().setFont(0, font) self.mAT1T1ITreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mAT1T1ITreeWidget.headerItem().setFont(3, font) self.mLFETreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mLFETreeWidget.setGeometry(QtCore.QRect(10, 660, 1031, 101)) self.mLFETreeWidget.setObjectName("mLFETreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mLFETreeWidget.headerItem().setFont(0, font) self.mLFETreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mLFETreeWidget.headerItem().setFont(3, font) self.mETC2TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mETC2TreeWidget.setGeometry(QtCore.QRect(10, 760, 1031, 101)) self.mETC2TreeWidget.setObjectName("mETC2TreeWidget") font = QtGui.QFont() font.setPointSize(12) self.mETC2TreeWidget.headerItem().setFont(0, font) self.mETC2TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mETC2TreeWidget.headerItem().setFont(3, font) self.tabWidget.addTab(self.tab_2, "") self.mComCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mComCfgBox.setGeometry(QtCore.QRect(1040, 10, 191, 231)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mComCfgBox.setFont(font) self.mComCfgBox.setObjectName("mComCfgBox") self.mPortName = QtWidgets.QLabel(self.mComCfgBox) self.mPortName.setGeometry(QtCore.QRect(20, 30, 61, 21)) self.mPortName.setObjectName("mPortName") self.mBpsName = QtWidgets.QLabel(self.mComCfgBox) self.mBpsName.setGeometry(QtCore.QRect(20, 60, 61, 21)) self.mBpsName.setObjectName("mBpsName") self.mDatabitName = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitName.setGeometry(QtCore.QRect(20, 90, 61, 21)) self.mDatabitName.setObjectName("mDatabitName") self.mStopName = QtWidgets.QLabel(self.mComCfgBox) self.mStopName.setGeometry(QtCore.QRect(20, 120, 61, 21)) self.mStopName.setObjectName("mStopName") self.mOddName = QtWidgets.QLabel(self.mComCfgBox) self.mOddName.setGeometry(QtCore.QRect(20, 150, 61, 21)) self.mOddName.setObjectName("mOddName") self.mDatabitVal = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitVal.setGeometry(QtCore.QRect(100, 90, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mDatabitVal.setFont(font) self.mDatabitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mDatabitVal.setAlignment(QtCore.Qt.AlignCenter) self.mDatabitVal.setObjectName("mDatabitVal") self.mStopBitVal = QtWidgets.QLabel(self.mComCfgBox) self.mStopBitVal.setGeometry(QtCore.QRect(100, 120, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mStopBitVal.setFont(font) self.mStopBitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mStopBitVal.setAlignment(QtCore.Qt.AlignCenter) self.mStopBitVal.setObjectName("mStopBitVal") self.mOddVal = QtWidgets.QLabel(self.mComCfgBox) self.mOddVal.setGeometry(QtCore.QRect(100, 150, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mOddVal.setFont(font) self.mOddVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mOddVal.setAlignment(QtCore.Qt.AlignCenter) self.mOddVal.setObjectName("mOddVal") self.mPortVal = QtWidgets.QComboBox(self.mComCfgBox) self.mPortVal.setGeometry(QtCore.QRect(90, 30, 81, 22)) self.mPortVal.setObjectName("mPortVal") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mPortVal.addItem("") self.mBPSVal = QtWidgets.QComboBox(self.mComCfgBox) self.mBPSVal.setGeometry(QtCore.QRect(90, 60, 81, 22)) self.mBPSVal.setObjectName("mBPSVal") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mBPSVal.addItem("") self.mOpenSerial = QtWidgets.QDialogButtonBox(self.mComCfgBox) self.mOpenSerial.setGeometry(QtCore.QRect(20, 190, 156, 31)) self.mOpenSerial.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Open) self.mOpenSerial.setObjectName("mOpenSerial") self.mCycleCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mCycleCfgBox.setGeometry(QtCore.QRect(1040, 260, 191, 221)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mCycleCfgBox.setFont(font) self.mCycleCfgBox.setObjectName("mCycleCfgBox") self.mcheck1000ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck1000ms.setGeometry(QtCore.QRect(20, 180, 141, 31)) self.mcheck1000ms.setObjectName("mcheck1000ms") self.mcheck500ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck500ms.setGeometry(QtCore.QRect(20, 150, 141, 31)) self.mcheck500ms.setObjectName("mcheck500ms") self.mcheck100ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck100ms.setGeometry(QtCore.QRect(20, 90, 141, 31)) self.mcheck100ms.setObjectName("mcheck100ms") self.mcheck50ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck50ms.setGeometry(QtCore.QRect(20, 60, 141, 31)) self.mcheck50ms.setObjectName("mcheck50ms") self.mcheck20ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck20ms.setGeometry(QtCore.QRect(20, 30, 141, 31)) self.mcheck20ms.setObjectName("mcheck20ms") self.mcheck200ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck200ms.setGeometry(QtCore.QRect(20, 120, 141, 31)) self.mcheck200ms.setObjectName("mcheck200ms") self.mEventSigBox = QtWidgets.QGroupBox(self.centralwidget) self.mEventSigBox.setGeometry(QtCore.QRect(1050, 490, 191, 151)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mEventSigBox.setFont(font) self.mEventSigBox.setObjectName("mEventSigBox") self.radioLeftREvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioLeftREvent.setGeometry(QtCore.QRect(10, 30, 151, 16)) self.radioLeftREvent.setObjectName("radioLeftREvent") self.radioKiilEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioKiilEvent.setGeometry(QtCore.QRect(10, 90, 151, 16)) self.radioKiilEvent.setObjectName("radioKiilEvent") self.radioPEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioPEvent.setGeometry(QtCore.QRect(10, 120, 151, 16)) self.radioPEvent.setObjectName("radioPEvent") self.radioOpenCloseEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioOpenCloseEvent.setGeometry(QtCore.QRect(10, 60, 151, 16)) self.radioOpenCloseEvent.setObjectName("radioOpenCloseEvent") self.mReadOBDinfBox = QtWidgets.QGroupBox(self.centralwidget) self.mReadOBDinfBox.setGeometry(QtCore.QRect(1050, 660, 191, 171)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mReadOBDinfBox.setFont(font) self.mReadOBDinfBox.setObjectName("mReadOBDinfBox") self.radioVinRead = QtWidgets.QRadioButton(self.mReadOBDinfBox) self.radioVinRead.setGeometry(QtCore.QRect(10, 40, 141, 21)) self.radioVinRead.setObjectName("radioVinRead") self.mVinInfShow = QtWidgets.QTextBrowser(self.mReadOBDinfBox) self.mVinInfShow.setGeometry(QtCore.QRect(10, 70, 171, 91)) self.mVinInfShow.setObjectName("mVinInfShow") MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) for num in range(0, 150, 1): item = self.tableWidget.verticalHeaderItem(num) item.setText(_translate("MainWindow", str(num +1))) item = self.tableWidget.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "时间标识")) item = self.tableWidget.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "帧ID")) item = self.tableWidget.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "帧类型")) item = self.tableWidget.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "长度")) item = self.tableWidget.horizontalHeaderItem(4) item.setText(_translate("MainWindow", "数据 (BIT7--BIT0 大端模式)")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1")) self.mSpeedTreeWidget.headerItem().setText(0, _translate("MainWindow", "速度[CCVS1]")) self.mSpeedTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mSpeedTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mSpeedTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(km/h)")) self.mSpeedTreeWidget.setColumnWidth(0, 150) self.mSpeedTreeWidget.setColumnWidth(1, 150) self.mSpeedTreeWidget.setColumnWidth(2, 550) self.mSpeedTreeWidget.setColumnWidth(3, 150) self.mRPMTreeWidget.headerItem().setText(0, _translate("MainWindow", "转速[EEC1]")) self.mRPMTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mRPMTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mRPMTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(rpm)")) self.mRPMTreeWidget.setColumnWidth(0, 150) self.mRPMTreeWidget.setColumnWidth(1, 150) self.mRPMTreeWidget.setColumnWidth(2, 550) self.mRPMTreeWidget.setColumnWidth(3, 150) self.mVDHRTreeWidget.headerItem().setText(0, _translate("MainWindow", "里程[VDHR]")) self.mVDHRTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mVDHRTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mVDHRTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(km)")) self.mVDHRTreeWidget.setColumnWidth(0, 150) self.mVDHRTreeWidget.setColumnWidth(1, 150) self.mVDHRTreeWidget.setColumnWidth(2, 550) self.mVDHRTreeWidget.setColumnWidth(3, 150) self.mHoursTreeWidget.headerItem().setText(0, _translate("MainWindow", "工作时长[HOURS]")) self.mHoursTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mHoursTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mHoursTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(hours)")) self.mHoursTreeWidget.setColumnWidth(0, 150) self.mHoursTreeWidget.setColumnWidth(1, 150) self.mHoursTreeWidget.setColumnWidth(2, 550) self.mHoursTreeWidget.setColumnWidth(3, 150) self.mEECTreeWidget.headerItem().setText(0, _translate("MainWindow", "发动机负载[EEC1]")) self.mEECTreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mEECTreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mEECTreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(%)")) self.mEECTreeWidget.setColumnWidth(0, 150) self.mEECTreeWidget.setColumnWidth(1, 150) self.mEECTreeWidget.setColumnWidth(2, 550) self.mEECTreeWidget.setColumnWidth(3, 150) self.mET1TreeWidget.headerItem().setText(0, _translate("MainWindow", "冷却液温度[ET1]")) self.mET1TreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mET1TreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mET1TreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(°)")) self.mET1TreeWidget.setColumnWidth(0, 150) self.mET1TreeWidget.setColumnWidth(1, 150) self.mET1TreeWidget.setColumnWidth(2, 550) self.mET1TreeWidget.setColumnWidth(3, 150) self.mAT1T1ITreeWidget.headerItem().setText(0, _translate("MainWindow", "燃油液面[AT1T1I]")) self.mAT1T1ITreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mAT1T1ITreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mAT1T1ITreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(%)")) self.mAT1T1ITreeWidget.setColumnWidth(0, 150) self.mAT1T1ITreeWidget.setColumnWidth(1, 150) self.mAT1T1ITreeWidget.setColumnWidth(2, 550) self.mAT1T1ITreeWidget.setColumnWidth(3, 150) self.mLFETreeWidget.headerItem().setText(0, _translate("MainWindow", "平均油耗[LFE]")) self.mLFETreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mLFETreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mLFETreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal(L/h)")) self.mLFETreeWidget.setColumnWidth(0, 150) self.mLFETreeWidget.setColumnWidth(1, 150) self.mLFETreeWidget.setColumnWidth(2, 550) self.mLFETreeWidget.setColumnWidth(3, 150) self.mETC2TreeWidget.headerItem().setText(0, _translate("MainWindow", "档位[ETC2]")) self.mETC2TreeWidget.headerItem().setText(1, _translate("MainWindow", "CanID")) self.mETC2TreeWidget.headerItem().setText(2, _translate("MainWindow", "Data")) self.mETC2TreeWidget.headerItem().setText(3, _translate("MainWindow", "Signal")) self.mETC2TreeWidget.setColumnWidth(0, 150) self.mETC2TreeWidget.setColumnWidth(1, 150) self.mETC2TreeWidget.setColumnWidth(2, 550) self.mETC2TreeWidget.setColumnWidth(3, 150) self.tableWidget.setColumnWidth(0, 200) self.tableWidget.setColumnWidth(1, 150) self.tableWidget.setColumnWidth(4,450) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "CanOBD Inf Show")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "CanOBD J1939 Show")) self.mComCfgBox.setTitle(_translate("MainWindow", "串口配置")) self.mPortName.setText(_translate("MainWindow", "端口号")) self.mBpsName.setText(_translate("MainWindow", "波特率")) self.mDatabitName.setText(_translate("MainWindow", "数据位")) self.mStopName.setText(_translate("MainWindow", "停止位")) self.mOddName.setText(_translate("MainWindow", "检验位")) self.mDatabitVal.setText(_translate("MainWindow", "8")) self.mStopBitVal.setText(_translate("MainWindow", "1")) self.mOddVal.setText(_translate("MainWindow", "无")) self.mBPSVal.setItemText(0, _translate("MainWindow", "9600")) self.mBPSVal.setItemText(1, _translate("MainWindow", "19200")) self.mBPSVal.setItemText(2, _translate("MainWindow", "115200")) self.mBPSVal.setItemText(3, _translate("MainWindow", "230400")) self.mBPSVal.setItemText(4, _translate("MainWindow", "256000")) self.mBPSVal.setItemText(5, _translate("MainWindow", "460800")) port_list = list(serial.tools.list_ports.comports()) if port_list.__len__() is not 0: for num in range(port_list.__len__()): self.mPortVal.setItemText(num, _translate("MainWindow", str(port_list[num].device))) serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial = SerialThread(serialport, serialbaudrate) self.mCycleCfgBox.setTitle(_translate("MainWindow", "过滤设置(周期)")) self.mcheck1000ms.setText(_translate("MainWindow", "1000ms 周期")) self.mcheck500ms.setText(_translate("MainWindow", "500ms 周期")) self.mcheck100ms.setText(_translate("MainWindow", "100ms 周期")) self.mcheck50ms.setText(_translate("MainWindow", "50ms 周期")) self.mcheck20ms.setText(_translate("MainWindow", "20ms 周期")) self.mcheck200ms.setText(_translate("MainWindow", "200ms 周期")) self.mEventSigBox.setTitle(_translate("MainWindow", "事件信号策略")) self.radioLeftREvent.setText(_translate("MainWindow", "左右转 事件")) self.radioKiilEvent.setText(_translate("MainWindow", "刹车 事件")) self.radioPEvent.setText(_translate("MainWindow", "档位 事件")) self.radioOpenCloseEvent.setText(_translate("MainWindow", "开关门 事件")) self.mReadOBDinfBox.setTitle(_translate("MainWindow", "主动读取信息")) self.radioVinRead.setText(_translate("MainWindow", "VIN 信息")) def OpenSerial(self): if self.LSerial != None: if self.LSerial.SerialIsOpen(): self.LSerial.__del__() port_list = list(serial.tools.list_ports.comports()) if port_list.__len__() != 0: serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial.__init__(serialport,serialbaudrate) # 开启线程 self.thread = Worker() # 创建线程对象 self.thread.update_signal.connect(self.CanOBDdatarefresh) # 连接信号和槽 self.thread.update_signal.connect(self.CanOBDSignalAnalyPro) # 连接信号和槽 # self.thread.update_signal.connect(self.LSerial.Com_read_frame) # 连接信号和槽 self.thread.start() # 启动线程 #self.LSerial.Com_read_frame() self.LSerial.start_reading() # <-- 在这里启动读取线程 def CloseSerial(self): if self.LSerial.SerialIsOpen(): self.LSerial.close() def Serialconnectslot(self): self.mOpenSerial.accepted.connect(self.OpenSerial) self.mOpenSerial.rejected.connect(self.CloseSerial) def get_checked_cycles(self): """返回用户勾选的所有周期值(毫秒)""" checked_cycles = [] if self.mcheck20ms.isChecked(): checked_cycles.append(10) if self.mcheck50ms.isChecked(): checked_cycles.append(50) if self.mcheck100ms.isChecked(): checked_cycles.append(100) if self.mcheck200ms.isChecked(): checked_cycles.append(200) if self.mcheck500ms.isChecked(): checked_cycles.append(500) if self.mcheck1000ms.isChecked(): checked_cycles.append(1000) return checked_cycles def CanOBDdatarefresh(self): # # 检查数据接收是否超时(1秒阈值) # current_time = time.time() # if current_time - self.LSerial.last_data_time > 1.0: # # 清空缓冲区并跳过刷新 # global frame_buffer # frame_buffer = bytearray() # return global CanOBDItemList # 声明模块全局变量 filtered_cycles = self.get_checked_cycles() with can_obd_lock: temp_obd_list = CanOBDItemList.copy() # 创建副本减少占用时间 all_update_ids = self.LSerial.data_updated_ids | self.LSerial.new_added_ids for can_id in all_update_ids: # 查找该ID在列表中的位置 row_index = None for idx, item in enumerate(temp_obd_list): if item[1] == can_id: row_index = idx break if row_index is None or row_index >= self.tableWidget.rowCount(): continue # 周期过滤检查 if can_id in self.LSerial.cycle_dict: cycle = self.LSerial.cycle_dict[can_id] skip = False for filtered_cycle in filtered_cycles: tolerance = filtered_cycle * 0.1 if abs(cycle - filtered_cycle) <= tolerance: skip = True break if skip: continue # 更新表格行 item_data = temp_obd_list[row_index] self.tableWidget.setItem(row_index, 0, QtWidgets.QTableWidgetItem(str(item_data[0]))) self.tableWidget.setItem(row_index, 1, QtWidgets.QTableWidgetItem(str(item_data[1]))) self.tableWidget.setItem(row_index, 2, QtWidgets.QTableWidgetItem(str(item_data[2]))) self.tableWidget.setItem(row_index, 3, QtWidgets.QTableWidgetItem(str(item_data[3]))) self.tableWidget.setItem(row_index, 4, QtWidgets.QTableWidgetItem(str(item_data[4]))) # 清空标志位 self.LSerial.data_updated_ids.clear() self.LSerial.new_added_ids.clear() self.tableWidget.show() def CanOBDSignalAnalyPro(self): global CanPGNItemList # 声明为全局变量 index = 0 bfindflag = 0 with can_pgn_lock: temp_pgn_list = CanPGNItemList.copy() # 创建副本 if all(not sublist for sublist in temp_pgn_list) or temp_pgn_list[0][0] == 0: if len(temp_pgn_list): temp_pgn_list.pop(0) else: for signalindex in temp_pgn_list: value = ''.join(c for c in signalindex[0].lower() if c in '0123456789abcdef') # 判断是否需要补零(前面补) if len(value) % 2 != 0: value = '0' + value # 将处理后的字符串重新赋值给 signalindex[0] signalindex[0] = value PGNCanID = bytes.fromhex(str(signalindex[0])).hex() #PGNCanID = int(signalindex[0]) #车速 if (PGNCanID == bytes.fromhex("FEF1").hex()): num_top_items = self.mSpeedTreeWidget.topLevelItemCount() for index in range(num_top_items ): if (self.mSpeedTreeWidget.topLevelItem(index).text(1) == str(signalindex[1])): self.mSpeedTreeWidget.topLevelItem(index).setText(0, str(signalindex[0])) self.mSpeedTreeWidget.topLevelItem(index).setText(1, str(signalindex[1])) self.mSpeedTreeWidget.topLevelItem(index).setText(2, str(signalindex[2])) self.mSpeedTreeWidget.topLevelItem(index).setText(3, str(signalindex[3])) bfindflag = 1 break #self.mSpeedTreeWidget.addTopLevelItem(speeditem) if bfindflag == 0: speeditem = QTreeWidgetItem(self.mSpeedTreeWidget) speeditem.setText(0, str(signalindex[0])) speeditem.setText(1, str(signalindex[1])) speeditem.setText(2, str(signalindex[2])) speeditem.setText(3, str(signalindex[3])) self.mSpeedTreeWidget.addTopLevelItem(speeditem) self.mSpeedTreeWidget.expandAll() self.mSpeedTreeWidget.show() with can_pgn_lock: CanPGNItemList= temp_pgn_list class Worker(QThread): update_signal = pyqtSignal(int) # 定义一个信号,用于传递更新信息到主线程 def run(self): # 模拟耗时操作 while True: time.sleep(0.1) self.update_signal.emit(1) # 发射信号,传递更新信息 这是canobd文件 我的程序在波特率115200时,读取can数据(每秒1200条)时会出现跳帧读取的现象,大概30帧会跳过10多帧,这是什么原因?
07-16
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值