0.背景
工程机械领域经常用到PCan来烧写程序或收发数据。前段时间接收到临时任务,需要模拟发动机发送故障代码。
已知条件:一份Excel格式的发动机故障代码文档,如图所示
发动机故障代码Excel文档
求解:利用PCan模拟出发动机发送故障代码的报文,做出PC端程序
通过以上分析,整理出开发思路如下:
1.读取Excel文件根据控制器的逻辑代码推理出原始的PCan报文,保存为txt格式,这个步骤容易实现。
2.开发PC端UI界面,具有转换Excel,选取txt发送,显示发送数据的功能,这个步骤容易实现。
3.利用PCan进行发送,这个步骤刚接到任务时感觉较难。
1.实现过程
开发出的界面如图所示,所利用到的知识PyQt5库与第2篇文章开发电影日历类似。
PC端带类型及地址软件界面
PC端软件发送中界面
具体功能就是1.读取txt文件按行进行周期间隔的时间发送;2.转换故障代码的Excel文件为符合发送规则的txt文件。
在读取txt文件发送的功能中实现可以不带类型及地址与带类型及地址发送两种模式,两种模式的txt格式如图所示
带/不带类型及地址的txt格式
利用python进行PCan的收发,搜索网上资源
https://www.haolizi.net/example/view_39351.html
下载该Demo进行代码阅读及修改,最后实现需要的功能,整体代码结构如图。
代码结构及用到的Demo
重要代码如下:
import os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PCANBasic import *
from threading import *
import xlrd
import time
from sys import *
temp250ms = 0
thread250ms=None
# pd=None
timesZq=0
textFilePath=''
textLines=[]
lineNum=0;
canType=0;
idAddress=0;
class HomeUi(QMainWindow):
def __init__(self,*args,**kwargs):
super(HomeUi,self).__init__(*args,**kwargs)
self.setWindowTitle('Pcan发送工具')
self.icon=QIcon('pcan01.jpg')
self.setWindowIcon(self.icon)
self.mainLayout = QGridLayout()
self.lab3_1 = QLabel()
self.lab3_1.setText('TXT文本格式')
self.combo_txt = QComboBox();
self.combo_txt.addItem('不带类型及地址')
self.combo_txt.addItem('带类型及地址')
self.combo_txt.activated[str].connect(self.comboTxtChange)
self.mainLayout.addWidget(self.lab3_1, 0, 0)
self.mainLayout.addWidget(self.combo_txt, 0, 1)
self.lab1=QLabel()
self.lab1.setText('请选择Txt文件')
self.mainLayout.addWidget(self.lab1,1,0)
self.btn1 = QPushButton()
self.btn1.setText('选择文件')
self.btn1.clicked.connect(self.openFile)
self.mainLayout.addWidget(self.btn1,1,1)
self.lab2 = QLabel()
self.lab2.setText('发送周期(ms)')
self.timesEditText = QLineEdit();
self.mainLayout.addWidget(self.lab2, 2, 0)
self.mainLayout.addWidget(self.timesEditText, 2, 1)
self.lab3 = QLabel()
self.lab3.setText('工作模式')
self.combo = QComboBox();
self.combo.addItem('标准')
self.combo.addItem('扩展')
self.combo.activated[str].connect(self.comboChange)
self.mainLayout.addWidget(self.lab3, 3, 0)
self.mainLayout.addWidget(self.combo, 3, 1)
self.lab4 = QLabel()
self.lab4.setText('地址(000-7FF)')
self.addressEditText = QLineEdit();
self.mainLayout.addWidget(self.lab4, 4, 0)
self.mainLayout.addWidget(self.addressEditText, 4, 1)
self.btnStop = QPushButton()
self.btnStop.setText('停止')
self.mainLayout.addWidget(self.btnStop, 5, 0)
self.btnStop.clicked.connect(self.stop)
self.btnSend = QPushButton()
self.btnSend.setText('发送')
self.mainLayout.addWidget(self.btnSend, 5, 1)
self.btnSend.clicked.connect(self.send)
self.tips = QLabel()
# self.tips.setText('12345678901234567890123456789012345678901234567890')
self.mainLayout.addWidget(self.tips,6,0,1,2)
self.lab1xx = QLabel()
self.mainLayout.addWidget(self.lab1xx, 7, 0)
self.lab1x = QLabel()
self.lab1x.setText('请选择Excel文件')
self.mainLayout.addWidget(self.lab1x, 8, 0)
self.btn1x = QPushButton()
self.btn1x.setText('选择excel文件')
self.btn1x.clicked.connect(self.openFilex)
self.mainLayout.addWidget(self.btn1x, 8, 1)
self.lab4x = QLabel()
self.lab4x.setText('C盘保存txt文件名')
self.savePathEditText = QLineEdit();
self.mainLayout.addWidget(self.lab4x, 9, 0)
self.mainLayout.addWidget(self.savePathEditText, 9, 1)
self.btnConvert = QPushButton()
self.btnConvert.setText('Excel转换为Text')
self.mainLayout.addWidget(self.btnConvert, 10, 1)
self.btnConvert.clicked.connect(self.convert)
widget = QWidget()
widget.setLayout(self.mainLayout)
self.setCentralWidget(widget)
# global position
# position=0
def convert(self):
print('convert')
filePath = self.lab1x.text()
if filePath=='请选择Excel文件':
msgBox = QMessageBox.information(self, '提示', '请选择Excel文件', QMessageBox.Yes)
else:
saveName = self.savePathEditText.text()
saveName=saveName.replace(' ','')
saveName = saveName.replace('\n', '')
if len(str(saveName))<=0:
msgBox = QMessageBox.information(self, '提示', '保存txt文件名不能为空', QMessageBox.Yes)
else:
print('开始保存')
execlFile = xlrd.open_workbook(filePath)
table = execlFile.sheet_by_name('HHP Tier 4 IND Fault Codes')
for i in range(1, table.nrows):
row = table.row_values(i)
print('code=' + str(int(row[0])) + ',spn=' + str(int(row[1])) + ',fmi=' + str(int(row[2])))
i = row[1]
data1 = 0;
data2 = 0;
data3 = 0;
data3 = int(i) / int(2048)
data2 = (int(i) - 2048 * int(data3)) / int(8)
data1 = int(i) - 2048 * int(data3) - int(data2) * 8
print(str(int(i)) + ':data1=' + str(int(data1)) + ',data2=' + str(int(data2)) + ',data3=' + str(
int(data3)))
fmi = row[2]
data_x = int(data1) * 32 + int(fmi)
print(str(int(i)) + ':data_x=' + str(int(data_x)) + ',data1=' + str(int(data1)) + ',data2=' + str(
int(data2)) + ',data3=' + str(int(data3)))
print('=================')
print('0202' + str(self.int2hex2(int(data3))) + str(self.int2hex2(int(data2))) + str(self.int2hex2(int(data_x))) + '80' + '0202')
with open('C:\\'+str(saveName)+'.txt', 'a', encoding='utf-8') as file:
file.write('0202' + str(self.int2hex2(int(data3))) + str(self.int2hex2(int(data2))) + str(self.int2hex2(int(data_x))) + '80' + '0202' + '\n')
msgBox = QMessageBox.information(self, '提示', 'Excel转txt成功', QMessageBox.Yes)
def int2hex2(self,num):
low = hex(int(num))
if len(str(low)) < 4:
values = str(low).split('x')
valueLow = '0' + values[1]
else:
valueLow = str(low).split('x')[1]
print(str(valueLow))
return str(valueLow)
def comboChange(self,text):
# 地址(000-7FF)
# 00000000-1FFFFFFF
# global position
if text=='标准':
# position=0
self.lab4.setText('地址(000-7FF)')
elif text=='扩展':
# global position
# position=1
self.lab4.setText('地址(00000000-1FFFFFFF)')
def comboTxtChange(self,text):
print('comboTxtChange:'+text)
if text == '不带类型及地址':
self.lab3.setVisible(True)
self.combo.setVisible(True)
self.lab4.setVisible(True)
self.addressEditText.setVisible(True)
else:
self.lab3.setVisible(False)
self.combo.setVisible(False)
self.lab4.setVisible(False)
self.addressEditText.setVisible(False)
def openFile(self):
openfileD = QFileDialog.getOpenFileName(self, '选择文件', '', 'Txt files(*.txt)')
print(openfileD)
if openfileD[0]=='':
print('请选择Txt文件')
self.lab1.setText('请选择Txt文件')
msgBox = QMessageBox.information(self,'提示','没有选择Text文件',QMessageBox.Yes)
# self.echo(msgBox)
else:
global textFilePath
textFilePath=openfileD[0]
print('textFilePath='+textFilePath)
self.lab1.setText(openfileD[0])
# msgBox = QMessageBox.information(self,'提示','选择txt文件成功',QMessageBox.Yes)
# self.echo(msgBox)
def openFilex(self):
openfileD = QFileDialog.getOpenFileName(self, '选择文件', '', 'Excel files(*.xlsx , *.xls)')
print(openfileD)
if openfileD[0]=='':
# print('请选择Txt文件')
self.lab1x.setText('请选择Excel文件')
msgBox = QMessageBox.information(self,'提示','没有选择Excel文件',QMessageBox.Yes)
# self.echo(msgBox)
else:
global textFilePath
textFilePath=openfileD[0]
print('textFilePath='+textFilePath)
self.lab1x.setText(openfileD[0])
def send(self):
filePath = self.lab1.text()
print('send'+filePath)
if filePath=='请选择Txt文件':
msgBox = QMessageBox.information(self, '提示', '请先选择Txt文件', QMessageBox.Yes)
else:
timeStr = self.timesEditText.text()
print(timeStr)
self.is_number(timeStr)
def stop(self):
print('stop')
global thread250ms
if thread250ms==None:
print('None')
else:
print('not None')
thread250ms.cancel()
thread250ms = None
self.btnSend.setEnabled(True)
self.combo.setEnabled(True)
self.combo_txt.setEnabled(True)
self.btn1.setEnabled(True)
def is_number(self,s):
try:
time = int(s)
if time<=0:
msgBox = QMessageBox.information(self, '提示', '周期数值不合法', QMessageBox.Yes)
else:
global timesZq
timesZq = int(s)
print('timesZq/1000='+str(timesZq/1000))
#判断地址和类型
# global position
# print("posotion="+position)
comboTxtType = self.combo_txt.currentText()
print('comboTxtType='+comboTxtType)
if comboTxtType=='带类型及地址':
self.combo.setEnabled(False)
self.combo_txt.setEnabled(False)
self.btnSend.setEnabled(False)
self.btn1.setEnabled(False)
self.sendMsg()
if comboTxtType=='不带类型及地址':
comboText = self.combo.currentText()
print('comboText='+comboText)
addressText = self.addressEditText.text()
if comboText=='标准':
print('进入标准校验')
global canType
canType=0;
print('canType='+str(canType))
try:
intAdd=int(str(addressText), 16)
print('intAdd='+str(intAdd))
if intAdd>=0 and intAdd<=0x7ff:
print('合法')
global idAddress
idAddress=intAdd
print('idAddress=' + str(idAddress))
self.btnSend.setEnabled(False)
self.combo.setEnabled(False)
self.combo_txt.setEnabled(False)
self.btn1.setEnabled(False)
self.sendMsg()
else:
msgBox = QMessageBox.information(self, '提示', '地址不合法', QMessageBox.Yes)
except ValueError:
print('error')
msgBox = QMessageBox.information(self, '提示', '地址不合法', QMessageBox.Yes)
pass
elif comboText=='扩展':
print('进入扩展校验')
# global canType
canType=1;
print('canType='+str(canType))
try:
intAdd=int(str(addressText), 16)
print('intAdd='+str(intAdd))
if intAdd>=0 and intAdd<=0x1FFFFFFF:
print('合法')
# global idAddress
idAddress=intAdd
print('idAddress=' + str(idAddress))
self.btnSend.setEnabled(False)
self.combo.setEnabled(False)
self.combo_txt.setEnabled(False)
self.btn1.setEnabled(False)
self.sendMsg()
else:
msgBox = QMessageBox.information(self, '提示', '地址不合法', QMessageBox.Yes)
except ValueError:
print('error')
msgBox = QMessageBox.information(self, '提示', '地址不合法', QMessageBox.Yes)
pass
except ValueError:
msgBox = QMessageBox.information(self, '提示', '周期数值不合法', QMessageBox.Yes)
pass
def sendMsg(self):
print('sendMsg')
global timesZq
print('timesZq=' + str(timesZq))
global pd
pd = PCANBasic()
pd.Initialize(PCAN_USBBUS1,PCAN_BAUD_250K)
global textLines
global textFilePath
textLines=[]
with open(textFilePath,'r') as file:
lines=file.readlines()
for line in lines:
if line=='':
print('结束了')
break
else:
textLines.append(line)
for i in range(len(textLines)):
print('textLines['+str(i)+']='+textLines[i])
global thread250ms
thread250ms = Timer(timesZq/1000, self.send250ms)
thread250ms.start()
#1.按行读取文件存本地数据库
#2.周期发送
def send250ms(self):
# global temp250ms
# temp250ms += 1
# if temp250ms > 8:
# temp250ms = 0
global lineNum
msg = TPCANMsg()
comboTxtType = self.combo_txt.currentText()
print('comboTxtType=' + comboTxtType)
if comboTxtType == '不带类型及地址':
print("idAddress="+str(idAddress)+',canType='+str(canType))
msg.ID = idAddress
# 0x004
if canType==0:
msg.MSGTYPE = PCAN_MODE_STANDARD
chCanType='标准'
elif canType==1:
msg.MSGTYPE = PCAN_MODE_EXTENDED
chCanType = '扩展'
msg.LEN = 8
# global textLines
sendItem = textLines[lineNum]
print('sendItem='+str(sendItem))
for i in range(8):
msg.DATA[i] = int(str(sendItem)[2*i:2*(i+1)],16)
print('sendItem['+str(2*i)+':'+str(2*(i+1))+']='+str(sendItem)[2*i:2*(i+1)])
print('msg.DATA['+str(i)+']='+str(msg.DATA[i]))
if lineNum>=len(textLines):
lineNum=0
else:
lineNum=lineNum+1
if lineNum>=len(textLines):
lineNum=0
global pd
pd.Write(PCAN_USBBUS1, msg)
# self.tips.setText('第'+str(lineNum+1)+'行:'+msg)
if lineNum==0:
hang=len(textLines)
else:
hang=lineNum
self.tips.setText('第' + str(hang) + '行:' +chCanType +'_'+str(msg.ID) + '_' + str(sendItem))
# print(250)
global timesZq
global thread250ms
thread250ms = Timer(timesZq/1000, self.send250ms)
thread250ms.start()
if comboTxtType=='带类型及地址':
print('带类型及地址')
sendItem = textLines[lineNum]
content = str(sendItem).split('_')
sendType = content[0]
sendId = content[1]
sendContent = content[2]
print('sendType='+str(sendType)+',sendId='+str(sendId)+',sendContent='+str(sendContent))
msg.LEN = 8
if str(sendType)=='0':
msg.MSGTYPE = PCAN_MODE_STANDARD
chCanType = '标准'
print('PCAN_MODE_STANDARD')
elif str(sendType) == '1':
msg.MSGTYPE = PCAN_MODE_EXTENDED
chCanType = '扩展'
print('PCAN_MODE_EXTENDED')
msg.ID = int(str(sendId),16)
print('msg.ID='+str(msg.ID))
print('sendContent=' + str(sendContent))
for i in range(8):
msg.DATA[i] = int(str(sendContent)[2 * i:2 * (i + 1)], 16)
print('sendContent[' + str(2 * i) + ':' + str(2 * (i + 1)) + ']=' + str(sendContent)[2 * i:2 * (i + 1)])
print('msg.DATA[' + str(i) + ']=' + str(msg.DATA[i]))
if lineNum >= len(textLines):
lineNum = 0
else:
lineNum = lineNum + 1
if lineNum >= len(textLines):
lineNum = 0
# global pd
if lineNum == 0:
hang = len(textLines)
else:
hang = lineNum
pd.Write(PCAN_USBBUS1, msg)
self.tips.setText('第' + str(hang) + '行:' +chCanType +'_'+str(msg.ID) + '_' + str(sendContent))
# print(250)
# global timesZq
# global thread250ms
thread250ms = Timer(timesZq / 1000, self.send250ms)
thread250ms.start()
def closeEvent(self, *args, **kwargs):
super().closeEvent(*args, **kwargs)
print("cccccccccccc")
global thread250ms
# print(thread250ms)
if thread250ms==None:
print('None')
else:
print('not None')
thread250ms.cancel()
thread250ms = None
网友评论