继续学习《CANoe开发从入门到精通》。
今天在《CANoe仿真工程开发》的基础上,开发实现pyqt5应用程序来操控CANoe工程。
COM(Component Object Model,组件对象模型)是一种描述如何建立可动态互变组件的规范,此规范提供了为保证能够互相操作,客户端和组件应遵循的一些二进制和网络标准。通过这种标准将可以在任意两个组件之间进行通信,而不用考虑其所处的操作环境是否相同、使用的开发语言是否一致以及是否运行于同一台计算机。
从早期版本开始,CANoe就开始支持COM接口技术。通过COM Server可以实现以下功能:
(1)创建和修改CANoe的配置;
(2)实现测量的自动控制,如工程加载、开始或结束测量、开始测试模块等;
(3)与外部应用软件的数据交换,如读写信号、系统变量等;
(4)开发用户的自定义面板,实现自动化测试;
(5)远程控制CANoe进行测量;
(6)调用CANoe中自定义的CAPL函数。
CANoe COM Server对于大家熟悉的编程语言或脚本语言都有很好的支持,例如Visual Basic、Delphi、C/C++、C#、Python、LabVIEW、VBScript、JScript、Perl和VBA等。
在CANoe安装时,COM Server已经注册好了。如果安装文件夹有变,或者目前注册的CANoe版本不是用户所期望的。可以直接找到CANoe 的安装文件夹( 例 如 CANoe 16 SP4 64-bit 安 装 文 件 夹 D:\Program Files\Vector CANoe 16\Exec64 ) , 执 行RegisterComponents.exe。
1、Pycharm新建工程;
2、右键,找到"External Tools",选择QT Designer,进入QT5的UI设计界面
3、添加相应的控件、设置好参数。最终的UI界面如下:
设计完成后,保存为CanoeDemo.ui。
4、回到PyCharm工程,CanoeDemo.ui文件,然后右键,找到"External Tools",选择PyUIC,将UI设计文件转换为CanoeDemo.py文件。
CanoeDemo.py文件如下:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'CanoeDemo.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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
class Ui_CanoeDemo(object):
def setupUi(self, CanoeDemo):
CanoeDemo.setObjectName("CanoeDemo")
CanoeDemo.resize(633, 508)
CanoeDemo.setMouseTracking(True)
CanoeDemo.setTabletTracking(True)
CanoeDemo.setAutoFillBackground(True)
self.groupBox = QtWidgets.QGroupBox(CanoeDemo)
self.groupBox.setGeometry(QtCore.QRect(10, 0, 531, 111))
self.groupBox.setObjectName("groupBox")
self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
self.lineEdit.setGeometry(QtCore.QRect(20, 20, 401, 20))
self.lineEdit.setObjectName("lineEdit")
self.OpenButton = QtWidgets.QPushButton(self.groupBox)
self.OpenButton.setGeometry(QtCore.QRect(440, 20, 75, 23))
self.OpenButton.setObjectName("OpenButton")
self.StartButton = QtWidgets.QPushButton(self.groupBox)
self.StartButton.setGeometry(QtCore.QRect(170, 60, 75, 23))
self.StartButton.setObjectName("StartButton")
self.AutoButton = QtWidgets.QPushButton(self.groupBox)
self.AutoButton.setGeometry(QtCore.QRect(300, 60, 75, 23))
self.AutoButton.setObjectName("AutoButton")
self.groupBox_2 = QtWidgets.QGroupBox(CanoeDemo)
self.groupBox_2.setGeometry(QtCore.QRect(10, 120, 531, 191))
self.groupBox_2.setTabletTracking(True)
self.groupBox_2.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.groupBox_2.setAcceptDrops(True)
self.groupBox_2.setObjectName("groupBox_2")
self.LockButton = QtWidgets.QPushButton(self.groupBox_2)
self.LockButton.setGeometry(QtCore.QRect(10, 30, 75, 23))
self.LockButton.setObjectName("LockButton")
self.UnlockButton = QtWidgets.QPushButton(self.groupBox_2)
self.UnlockButton.setGeometry(QtCore.QRect(90, 30, 75, 23))
self.UnlockButton.setObjectName("UnlockButton")
self.Driver1Button = QtWidgets.QPushButton(self.groupBox_2)
self.Driver1Button.setGeometry(QtCore.QRect(10, 60, 75, 23))
self.Driver1Button.setObjectName("Driver1Button")
self.Driver2Button = QtWidgets.QPushButton(self.groupBox_2)
self.Driver2Button.setGeometry(QtCore.QRect(90, 60, 75, 23))
self.Driver2Button.setObjectName("Driver2Button")
self.LeftButton = QtWidgets.QPushButton(self.groupBox_2)
self.LeftButton.setGeometry(QtCore.QRect(10, 110, 75, 23))
self.LeftButton.setObjectName("LeftButton")
self.RightButton = QtWidgets.QPushButton(self.groupBox_2)
self.RightButton.setGeometry(QtCore.QRect(90, 110, 75, 23))
self.RightButton.setObjectName("RightButton")
self.HazardButton = QtWidgets.QPushButton(self.groupBox_2)
self.HazardButton.setGeometry(QtCore.QRect(50, 140, 75, 23))
self.HazardButton.setObjectName("HazardButton")
self.IgniSlider = QtWidgets.QSlider(self.groupBox_2)
self.IgniSlider.setGeometry(QtCore.QRect(300, 20, 22, 160))
self.IgniSlider.setMaximum(3)
self.IgniSlider.setPageStep(1)
self.IgniSlider.setOrientation(QtCore.Qt.Vertical)
self.IgniSlider.setObjectName("IgniSlider")
self.GearSlider = QtWidgets.QSlider(self.groupBox_2)
self.GearSlider.setGeometry(QtCore.QRect(430, 20, 22, 160))
self.GearSlider.setMouseTracking(False)
self.GearSlider.setTabletTracking(True)
self.GearSlider.setMaximum(3)
self.GearSlider.setPageStep(1)
self.GearSlider.setProperty("value", 0)
self.GearSlider.setSliderPosition(0)
self.GearSlider.setOrientation(QtCore.Qt.Vertical)
self.GearSlider.setObjectName("GearSlider")
self.label = QtWidgets.QLabel(self.groupBox_2)
self.label.setGeometry(QtCore.QRect(460, 170, 54, 12))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(self.groupBox_2)
self.label_2.setGeometry(QtCore.QRect(460, 120, 54, 12))
self.label_2.setObjectName("label_2")
self.label_3 = QtWidgets.QLabel(self.groupBox_2)
self.label_3.setGeometry(QtCore.QRect(460, 70, 54, 12))
self.label_3.setObjectName("label_3")
self.label_4 = QtWidgets.QLabel(self.groupBox_2)
self.label_4.setGeometry(QtCore.QRect(460, 20, 54, 12))
self.label_4.setObjectName("label_4")
self.label_5 = QtWidgets.QLabel(self.groupBox_2)
self.label_5.setGeometry(QtCore.QRect(400, 20, 31, 16))
self.label_5.setObjectName("label_5")
self.label_6 = QtWidgets.QLabel(self.groupBox_2)
self.label_6.setGeometry(QtCore.QRect(330, 70, 54, 12))
self.label_6.setObjectName("label_6")
self.label_7 = QtWidgets.QLabel(self.groupBox_2)
self.label_7.setGeometry(QtCore.QRect(250, 20, 54, 12))
self.label_7.setObjectName("label_7")
self.label_8 = QtWidgets.QLabel(self.groupBox_2)
self.label_8.setGeometry(QtCore.QRect(330, 20, 54, 12))
self.label_8.setObjectName("label_8")
self.label_9 = QtWidgets.QLabel(self.groupBox_2)
self.label_9.setGeometry(QtCore.QRect(330, 170, 54, 12))
self.label_9.setObjectName("label_9")
self.label_10 = QtWidgets.QLabel(self.groupBox_2)
self.label_10.setGeometry(QtCore.QRect(330, 120, 54, 12))
self.label_10.setObjectName("label_10")
self.groupBox_3 = QtWidgets.QGroupBox(CanoeDemo)
self.groupBox_3.setGeometry(QtCore.QRect(10, 320, 531, 121))
self.groupBox_3.setObjectName("groupBox_3")
self.label_11 = QtWidgets.QLabel(self.groupBox_3)
self.label_11.setGeometry(QtCore.QRect(20, 30, 91, 16))
self.label_11.setObjectName("label_11")
self.label_12 = QtWidgets.QLabel(self.groupBox_3)
self.label_12.setGeometry(QtCore.QRect(20, 80, 81, 16))
self.label_12.setObjectName("label_12")
self.VehSlider = QtWidgets.QSlider(self.groupBox_3)
self.VehSlider.setGeometry(QtCore.QRect(110, 30, 160, 22))
self.VehSlider.setMaximum(220)
self.VehSlider.setSingleStep(5)
self.VehSlider.setPageStep(10)
self.VehSlider.setOrientation(QtCore.Qt.Horizontal)
self.VehSlider.setObjectName("VehSlider")
self.EngSlider = QtWidgets.QSlider(self.groupBox_3)
self.EngSlider.setGeometry(QtCore.QRect(110, 80, 160, 22))
self.EngSlider.setMaximum(5000)
self.EngSlider.setSingleStep(10)
self.EngSlider.setPageStep(40)
self.EngSlider.setOrientation(QtCore.Qt.Horizontal)
self.EngSlider.setObjectName("EngSlider")
self.VehSpeedEdit = QtWidgets.QLineEdit(self.groupBox_3)
self.VehSpeedEdit.setGeometry(QtCore.QRect(290, 30, 113, 20))
self.VehSpeedEdit.setObjectName("VehSpeedEdit")
self.EngSpeedEdit = QtWidgets.QLineEdit(self.groupBox_3)
self.EngSpeedEdit.setGeometry(QtCore.QRect(290, 80, 113, 20))
self.EngSpeedEdit.setObjectName("EngSpeedEdit")
self.label_13 = QtWidgets.QLabel(self.groupBox_3)
self.label_13.setGeometry(QtCore.QRect(420, 32, 54, 20))
self.label_13.setObjectName("label_13")
self.label_14 = QtWidgets.QLabel(self.groupBox_3)
self.label_14.setGeometry(QtCore.QRect(420, 80, 54, 20))
self.label_14.setObjectName("label_14")
self.retranslateUi(CanoeDemo)
QtCore.QMetaObject.connectSlotsByName(CanoeDemo)
def retranslateUi(self, CanoeDemo):
_translate = QtCore.QCoreApplication.translate
CanoeDemo.setWindowTitle(_translate("CanoeDemo", "Form"))
self.groupBox.setTitle(_translate("CanoeDemo", "应用控制"))
self.OpenButton.setText(_translate("CanoeDemo", "打开CANoe"))
self.StartButton.setText(_translate("CanoeDemo", "开始测量"))
self.AutoButton.setText(_translate("CanoeDemo", "自动测量"))
self.groupBox_2.setTitle(_translate("CanoeDemo", "系统变量"))
self.LockButton.setText(_translate("CanoeDemo", "Lock"))
self.UnlockButton.setText(_translate("CanoeDemo", "Unlock"))
self.Driver1Button.setText(_translate("CanoeDemo", "Driver1"))
self.Driver2Button.setText(_translate("CanoeDemo", "Driver2"))
self.LeftButton.setText(_translate("CanoeDemo", "Leftturn"))
self.RightButton.setText(_translate("CanoeDemo", "Rightturn"))
self.HazardButton.setText(_translate("CanoeDemo", "Hazard"))
self.label.setText(_translate("CanoeDemo", "P"))
self.label_2.setText(_translate("CanoeDemo", "R"))
self.label_3.setText(_translate("CanoeDemo", "N"))
self.label_4.setText(_translate("CanoeDemo", "D"))
self.label_5.setText(_translate("CanoeDemo", "Gear:"))
self.label_6.setText(_translate("CanoeDemo", "RUN"))
self.label_7.setText(_translate("CanoeDemo", "Ignition:"))
self.label_8.setText(_translate("CanoeDemo", "CRANK"))
self.label_9.setText(_translate("CanoeDemo", "OFF"))
self.label_10.setText(_translate("CanoeDemo", "K15"))
self.groupBox_3.setTitle(_translate("CanoeDemo", "GroupBox"))
self.label_11.setText(_translate("CanoeDemo", "Vehicle Speed:"))
self.label_12.setText(_translate("CanoeDemo", "Engine Speed:"))
self.label_13.setText(_translate("CanoeDemo", "km/h"))
self.label_14.setText(_translate("CanoeDemo", "rpm"))
到此,界面设计完成。
1、通过打开CANoe按钮实现CANoe文件的选择、打开、和关闭。代码如下:
def open_cfg(self):
if self.OpenButton.text() == "打开CANoe":
openfile_name = QFileDialog.getOpenFileName(self,'选择文件','','(*.cfg)')
file_name = openfile_name[0]
if file_name:
print(file_name)
self.App.Open(file_name)
self.lineEdit.setText(file_name)
else:
print("Open CANoe error")
self.OpenButton.setText("退出CANoe")
elif self.OpenButton.text() == "退出CANoe":
if self.App != None:
self.App.Quit()
self.App = None
self.OpenButton.setText("打开CANoe")
实现效果如下:
2、开始测量按钮,实现仿真测试的开始、关闭。代码如下:
def start(self):
if self.StartButton.text() == "开始测量":
self.App.Measurement.Start()
self.StartButton.setText("停止测量")
elif self.StartButton.text() == "停止测量":
self.App.Measurement.Stop()
self.StartButton.setText("开始测量")
3、其他按钮,主要实现操作总线信号和系统变量来实现控制CANoe的信号和系统变量。比如Lock按钮,先读取系统变量Lock_Car的值,再来设置它的值。代码如下:
def lock(self):
if(self.get_SysVar("Vehicle_Key","Lock_Car")==0):
self.set_SysVar("Vehicle_Key","Lock_Car",1)
time.sleep(1)
self.set_SysVar("Vehicle_Key", "Lock_Car", 0)
else:
time.sleep(1)
self.set_SysVar("Vehicle_Key", "Lock_Car", 0)
实现了UI这边点击一下Lock按钮,CANoe的钥匙上Lock图标变红,一秒后恢复。
4、自动测量,这个通过一个按钮实现了CANoe的Automation Sequences功能。
可以看后面的演示视频,Vehicle(Automation Sequences)的RepeatSequence periodically图标和Start Sequence on measurement start图标(如上红框)并未选中,就依次实现了上面的功能。
5、获取和设置系统变量,代码如下:
def get_SysVar(self, ns_name, sys_name):
if self.App != None:
systemCAN = self.App.System.Namespaces
sys_namespace = systemCAN(ns_name)
sys_value = sys_namespace.Variables(sys_name)
return sys_value.Value
else:
print("Unable to get EnvVar")
def set_SysVar(self, ns_name, sys_name, value):
if self.App != None:
systemCAN = self.App.System.Namespaces
sys_namespace = systemCAN(ns_name)
sys_value = sys_namespace.Variables(sys_name)
sys_value.Value = value
else:
print("Unable to set EnvVar")
编译运行工程,然后开始测试,演示结果如下:
Python调用CANoe