安装python环境
https://www.python.org/downloads/windows/
安装pyinstaller
pip install pyinstaller
准备python源码
from tkinter import Tk
from tkinter import Button
from tkinter import INSERT
from tkinter import END
from tkinter import WORD
from tkinter import BOTH
from tkinter.scrolledtext import ScrolledText
import tkinter.filedialog
import tkinter.messagebox
import xml.dom.minidom
import os
import re
import string
g_timeout = 4 # 4秒超时
# 通讯参数
g_param = {
"ProtocolType": "0x00",
"PHYPort": "0x00",
"FunctionAddress": "0x00",
"ECUAddress": "0x00",
"TesterAddress": "0xF1",
"Baudrate": "500",
"TIdle": "300",
"TWup": "50",
"P1Min": "0",
"P1Max": "20",
"P2Min": "25",
"P2Max": "50",
"P3Min": "55",
"P3Max": "5000",
"P4Min": "5",
"P4Max": "20",
"CanPhysReqId": "0x710",
"CanFuncReqId": "0x7DF",
"Equipment_Serv_Id": "0x00",
"Ecu_Time_T1": "0x00",
"Ecu_Time_T2": "0x00",
"Ecu_Time_T3": "0x00",
"Ecu_Time_T4": "0x00",
"Tester_Time_T1": "0x00",
"Tester_Time_T2": "0x00",
"Tester_Time_T3": "0x00",
"Tester_Time_T4": "0x00",
}
# 日志中解析的报文结构
class Packet:
id = ""
thread = ""
process = ""
time = 0
content = []
content_string = ""
valid = True
def __init__(self, id, thread, process ,content, content_string, time):
self.id = id
self.thread = thread
self.process = process
self.content = content
self.content_string = content_string
self.time = time
# 对应xml
class Service:
req = ""
res = ""
def __init__(self, req, res):
self.req = req
self.res = res
# 对应xml
class Section:
name = ""
sid = ""
rid = ""
services = []
def __init__(self, sid, rid):
self.name = sid + "_" + rid
self.sid = sid
self.rid = rid
self.services = []
class Main():
packets = []
xmlFile = []
def __init__(self):
self.win = Tk()
self.initWin()
self.log = ScrolledText(self.win, wrap=WORD, height=40)
self.log.pack(side="bottom", fill=BOTH)
self.initBtn()
self.win.mainloop()
def initWin(self):
self.win.title("Log")
self.win.geometry("600x600")
self.win.resizable(width=False,height=False)
def initBtn(self):
Button(self.win, text="选择日志文件..", command=self.GetResource).place(x=10, y=10)
Button(self.win, text="导出结果文件", command=self.GenerateXML).place(x=110, y=10)
# 打开文件 获取所有报文内容
def GetResource(self):
default_dir = r"C:\"
self.filePath = tkinter.filedialog.askopenfilename(title=u'选择文件', initialdir=(os.path.expanduser(default_dir)))
try:
f= open(self.filePath, 'r', encoding='UTF-8', errors='ignore')
except IOError as e:
Tk.messagebox.showerror(title = '错误', message='打开文件失败')
return
patternPacket = re.compile('^[A-Z]+ ([0-9]*) (?P<time>[0-9]*).([0-9]*) (?P<thread_num>[a-zA-Z0-9]*)::(?P<process_num>[a-zA-Z0-9]*) [([a-zA-Z]*)] OS[<|>] (?P<id>[a-zA-Z0-9]*) (?P<content>[^"]*)')
patternOdbParam = re.compile('^[A-Z]+ ([0-9]*) ([0-9]*).([0-9]*) ([a-zA-Z0-9]*)::([a-zA-Z0-9]*) [([a-zA-Z]*)] ObdOpen(port=([0-9]*),proto=([0-9]*),rate=([a-zA-Z0-9]*),fadr=([a-zA-Z0-9]*),eadr=([a-zA-Z0-9]*),tadr=([a-zA-Z0-9]*),sid=0x(?P<sid>[a-zA-Z0-9]*),rid=0x(?P<rid>[a-zA-Z0-9]*))')
self.log.delete(0.0, END)
lines = f.readlines()
for line in lines:
if (-1 != line.find("[vcicore] ObdOpen(port=")):
regMatch = patternOdbParam.match(line)
if None != regMatch:
linebits = regMatch.groupdict()
sid = linebits['sid']
rid = linebits['rid']
# 去重
if (self.RemoveDuplication(sid, rid)):
section = Section(sid, rid)
self.xmlFile.append(section)
if (-1 != line.find("[vcicore] OS")):
regMatch = patternPacket.match(line)
if None != regMatch:
linebits = regMatch.groupdict()
content_string = content = linebits['content'].replace("
","")
content = content_string.split(' ')
# 36刷写报文只取部分
if (len(content) > 20
and content[0] == "36"):
content = content[:3]
content_string = content_string[:8]
packet = Packet(linebits['id'], linebits['thread_num'], linebits['process_num'], content, content_string, int(linebits['time']))
self.log.insert(INSERT, linebits['id']+ " " +content_string +'
')
self.packets.append(packet)
f.close()
# 生成xml文件
def GenerateXML(self):
cnt = 0
for element in self.xmlFile:
self.Organize(element)
if (len(element.services) != 0):
self.CreateFile(element)
cnt = cnt + 1
tkinter.messagebox.showwarning(title = '完成',message='共生成'+str(cnt)+'个文件')
self.filePath = ""
# 判断是否重复
def RemoveDuplication(self, sid, rid):
for e in self.xmlFile:
if (sid == e.sid and rid == e.rid):
return False
return True
# 验证是否是对应的收发报文
def Calibrate(self, req, res):
if (req.process == res.process and req.thread == res.thread):
if (len(req.content) > 0 and len(res.content) > 0):
if (res.content[0] == "7F"): #7F的特殊情况
return True
if (64 == int(res.content[0], 16) - int(req.content[0], 16)):
return True
return False
# 整理报文对应的文件
def Organize(self, section):
cnt_record = 0
while (True):
if (0 == len(self.packets)):
break # 报文列表为空,退出
#找到一个发送id
cnt_sid = -1
index = cnt_record
while (index < len(self.packets)):
if (self.packets[index].valid
and section.sid == self.packets[index].id):
cnt_sid = index
#找到的发送报文设置无效标记
self.packets[index].valid = False
break
index += 1
cnt_record = index
#sid未找到,列表中没有需要的报文,退出
if (-1 == cnt_sid):
break
#找到报文发送id对应的接收id
cnt_rid = -1
index = cnt_sid + 1
sid_head = self.packets[cnt_sid].content[0] #发送报文的头部
max_time = self.packets[cnt_sid].time + g_timeout #最大寻找时间
while (index < len(self.packets)):
if (self.packets[index].valid
and section.rid == self.packets[index].id
and self.packets[index].time < max_time):
#判断是延时帧(7F xx 78)
if (len(self.packets) > 2
and self.packets[index].content[0] == "7F"
and self.packets[index].content[1] == sid_head
and self.packets[index].content[2] == "78"):
max_time = self.packets[index].time + g_timeout # 更新超时时间,以当前78报文为准
self.packets[index].valid = False # 78报文设置无效标记
index += 1
continue
if (self.Calibrate(self.packets[cnt_sid], self.packets[index])): # 通过线程号等验证是否是对应的接收报文
cnt_rid = index # 找到了接收的报文
break
index += 1
#rid未找到,超时
if (-1 == cnt_rid):
continue
#添加列表
svc = Service(self.packets[cnt_sid].content_string, self.packets[cnt_rid].content_string)
self.log.see(END)
section.services.append(svc)
self.packets[cnt_rid].valid = False
# 创建xml文件
def CreateFile(self, resultFile):
impl = xml.dom.minidom.getDOMImplementation()
# 设置根结点
dom = impl.createDocument(None, 'ECU', None)
root = dom.documentElement
root.setAttribute("name", resultFile.name)
# 根节点添加子节点COMMPARAMETERS
commparameters = dom.createElement('COMMPARAMETERS')
root.appendChild(commparameters)
# 设置通讯参数
for key, value in g_param.items():
nameE=dom.createElement('PARAM')
nameE.setAttribute("name", key)
nameE.setAttribute("value", value)
commparameters.appendChild(nameE)
# 设置canid
nameE=dom.createElement('PARAM')
nameE.setAttribute("name", "CANSID")
nameE.setAttribute("value",resultFile.sid)
commparameters.appendChild(nameE)
nameE=dom.createElement('PARAM')
nameE.setAttribute("name", "CANRID")
nameE.setAttribute("value", resultFile.sid)
commparameters.appendChild(nameE)
# 根节点添加子节点SECTIONS
sections = dom.createElement('SECTIONS')
root.appendChild(sections)
# SECTION
section=dom.createElement('SECTION')
sections.appendChild(section)
for svc in resultFile.services:
#SERVICE
service=dom.createElement('SERVICE')
section.appendChild(service)
#REQ
req=dom.createElement('REQ')
reqTxt=dom.createTextNode(svc.req) #内容
req.appendChild(reqTxt)
service.appendChild(req)
#RES
res=dom.createElement('RES')
resTxt=dom.createTextNode(svc.res) #内容
res.appendChild(resTxt)
service.appendChild(res)
f= open(resultFile.name + '.xml', 'w') #w替换为a,追加
dom.writexml(f, addindent=' ', newl='
')
f.close()
if __name__ == '__main__':
Main()
打包程序
# -w 隐藏控制台
pyinstaller -F -w program.py