3.Python开发PCan周期发送数据

0.背景

工程机械领域经常用到PCan来烧写程序或收发数据。前段时间接收到临时任务,需要模拟发动机发送故障代码。
已知条件:一份Excel格式的发动机故障代码文档,如图所示


发动机故障代码Excel文档

求解:利用PCan模拟出发动机发送故障代码的报文,做出PC端程序
通过以上分析,整理出开发思路如下:
1.读取Excel文件根据控制器的逻辑代码推理出原始的PCan报文,保存为txt格式,这个步骤容易实现。
2.开发PC端UI界面,具有转换Excel,选取txt发送,显示发送数据的功能,这个步骤容易实现。
3.利用PCan进行发送,这个步骤刚接到任务时感觉较难。

1.实现过程

开发出的界面如图所示,所利用到的知识PyQt5库与第2篇文章开发电影日历类似。

PC端软件启动后界面

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

你可能感兴趣的:(3.Python开发PCan周期发送数据)