为满足项目过程中不同阶段绝大部分测试需求,更方便快捷构造测试场景,支持异常场景测试。更早介入,不依赖周边ECU的稳定情况,专注于被测ECU。更经济,不加油,不充电,时间节省,物料节省等维度考虑。我们需要一个建设测试台架至少可覆盖实车上80%的测试场景需求。

目标任务分解

1、车内网络模型建立

模拟车内网络通信模型,各节点信号仿真

【车联网T-BOX】基于CANoe实现的远程服务业务自动化测试方案-LMLPHP

2、业务关联的ECU仿真

封装ECU之间的业务交互逻辑,车身控制器,仪表台模拟等

a)每个ECU的逻辑都是随CANoe启动,激活CAN通信;

b)根据PEPS电源信号状态决定该ECU的活跃状态;

c)ECU 根据具体业务处理总线上的请求;

d)设计仿真器的ECU则根据信号变化情况,更新仿真器的状态

CAPL编程实现

1、环境变量

为实现控制面板输入输出与信号同步,实现仿真器的状态更新,先定义与信号成映射关系的环境变量。环境变量的定义主要根据各ECU的相关信号与业务的关联度决定。基本上与T业务挂钩的信号都应该设置相应的环境变量,监控信号变化,实时更新仿真器的状态。

2、各ECU通用代码块

存储一些全局变量,日志记录等,各ECU可包含此文件调用

variables
{
// 报文发送周期, 单位ms
const int varCycTime10 = ;
const int varCycTime20 = ;
const int varCycTime50 = ;
const int varCycTime100 = ;
const int varCycTime200 = ;
const int varCycTime500 = ;
const int varCycTime1000 = ; // varCarType车型定义, 0=纯油, 1=纯电, 2=混动, others=error
// 字母代码 AFV=纯油, EV=纯电, HEV=混动(不区分直插式和充电桩式)
// 全局LOG
long mTrace = ;
//severity
dword INFO = ;
dword WARN = ;
dword ERROR = ;
// 鉴权秘钥
byte varESKCode[][] = {
//。。。略
}; // varCarCode 标记当前被测车型, 用于选择调用正确的鉴权码
int varESKNumber;
// 记录当前电源模式,0=OFF; 1=ACC; 2=ON; 3=reserved; 4=start; 5,6,7=reserved
int varPowerMode=;
int lastPowerMode=;
// 发动机状态
int varEngineStatus; //总线报文管理,0=停发, 1=启动
int varNM;// 远程控制请求中,启动发动机的时间
int varRmStartTime;
// 远程控制请求中,启动发动机的时间长度 分级
int varRmStartTimeLvl; ////0=No Req; 1=3min; 2=5min; 3=8min; 4=10min; 5,6,7=reserved
// // 防盗报警定时器
// timer BCM_ATWS_timer;
//车速
int varVelSpeed; //标记发动机是否已启动
int IsEngineWorking = ;
} /***********************************************************
* description : 全局日志记录函数
* parameter : None
* creation date: 2018/10/26
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
void InitLogging()
{
mTrace = writeCreate("Logging");
writeConfigure(mTrace,*,,"..\\Proj\\Log\\write.txt");
writeclear();
}

3、ESC封装车速信号,处理行车过程中自动落锁的逻辑

此处详尽展现代码实现段落开始处所描述的四点逻辑,后续ECU只展现具体的业务处理,不在代码展示中继续保留a)&  b),或 d)

includes
{
#include "ECUsVar.can"
} variables
{
//本消息由ESC发出,包含ESC状态,车速、以及刹车板状态信号, 此处ID由我捏造为0x111
message 0x111 ESC_Status;
msTimer ESC_timer;
} on timer ESC_timer
{
output(ESC_Status);
setTimer(ESC_timer, varCycTime20);
} //车速
on envVar ESC_VehicleSpeed
{
float factor = 0.05625;
int offset = ;
varVelSpeed = getValue(ESC_VehicleSpeed); //转换成仪表显示, KPH
if(varPowerMode!=)
{
writeDbgLevel(, "PowerMode=OFF,车速调节无效");
ESC_Status.ESC_VehicleSpeed = ;
putValue(ESC_VehicleSpeed, );
}
else
{
if(varVelSpeed!=) //置位发动机转速
{
writeDbgLevel(, "点火状态,车速不为零,设置发动机转速");
putValue(EMS_EngineSpeedRPM,);
}
else
{
putValue(EMS_EngineSpeedRPM,);
}
if(varVelSpeed>) //车速>25时,自动落锁
{
writeDbgLevel(,"车速>25码,自动落锁");
putValue(LockDoors, );
}
ESC_Status.ESC_VehicleSpeed = (varVelSpeed-offset)/ factor;
}
} on envVar PEPS_PowerMode
{
// 此处根据PEPS电源状态封装ESC在网络上的活跃情况(是否对外发送信号和处理总线上的请求)
} on start
{
InitESCValue();
ActiveESC();
} //初始化
void InitESCValue()
{
varPowerMode = getValue(PEPS_PowerMode);
//以下两行代码初始化ESC信号,此处略去其它很多信号,自填......
putValue(ESC_VehicleSpeed, );
ESC_Status.xxx=;
} //激活
void ActiveESC()
{
setTimer(ESC_timer, varCycTime20);
}
//去激活
void InactiveESC()
{
cancelTimer(ESC_timer);
}

4、IPK实现同步系统时间

若你的车联网系统的标准时钟来自其它ECU,则以该ECU的时间为参考。这涉及判断T业务的实时性与有效性逻辑。(注意:我所用的CANoe 8.5.98的调用getLocalTimeString获取系统时间异常,我通过代码矫正了一下,11.0版本则不用矫正)

variables
{
const h_offset = ;//5; //时差
const m_offset = ;//5; //分差
// IPK提供日期时间信息, 年月日,时分秒;500ms
message 0x222 IPK_DateTime; //以下报文ID全由我捏造,可根据实际DBC中定义修改成相应ID // IPK提供纯电续航里程、平均电耗、瞬时电耗、油耗, 1000ms
message 0x223 IPK_Data; // 本消息由IPK发出,包含仪表的信息,100ms
// 安全气囊控制器状态检查反馈,剩余油量,平均车速,手刹状态,保养提示报警,背光调节,机油压力低报警状态
message 0x224 IPK_STS; // 本消息由IPK发出,包含总里程的信息,可续航里程,保养里程;1000ms
message 0x225 IPK_Odometer; msTimer IPK_Date_timer;
msTimer IPK_ODO_timer;
msTimer IPK_Sts_timer;
//当前时间提示器, 用来核对TBOX系统时间是否正确
timer DateRemanderTimer; // 存放时间的数组,当IPK负载不存在时,仿真实现。传递时间到TBOX
long tm[];
char t_canoe[];
char t_now[];
}

***********************************************************
* description : 由IPK发出的系统时间,传递给TBOX同步此时间
* parameter : None
* creation date: 2018/10/15
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
on timer IPK_Date_timer
{
output(IPK_DateTime);
setTimer(IPK_Date_timer, varCycTime500);
} /***********************************************************
* description : 由IPK发出的仪表盘信息
* parameter : None
* creation date: 2018/10/15
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
on timer IPK_Sts_timer
{
GetSysTime();
output(IPK_STS);
setTimer(IPK_Sts_timer, varCycTime100);
} /***********************************************************
* description : 由IPK发出的里程相关信息,可能会根据油车,混动,纯电,展示的信息不一致,根据DBC定义来实现
* parameter : None
* creation date: 2018/10/15
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
on timer IPK_ODO_timer
{
output(IPK_Odometer);
setTimer(IPK_AFV_ODO_timer, varCycTime1000);
}/***********************************************************
* description : 获取当前PC时间,作为IPK时间发布到CAN线上
* parameter : None
* creation date: 2018/10/15
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
void GetSysTime()
{
getLocalTimeString(t_canoe); getLocalTime(tm);
// year since 1900; month from 0-11
IPK_DateTime.IPK_Year = tm[]-;
IPK_DateTime.IPK_Month = tm[]+; IPK_DateTime.IPK_Second = tm[];
//以上API获取的时间比北京时间快6h 5min
if(tm[]>=h_offset) //24小时制
{
IPK_DateTime.IPK_Hour = tm[]-h_offset; //减去快的6H
IPK_DateTime.IPK_Day = tm[];
}
else
{
IPK_DateTime.IPK_Hour = tm[] -h_offset+; //当时间跳到第二天凌晨,逆向+18
IPK_DateTime.IPK_Day = tm[] -; // day-1
}
if(tm[]>=m_offset) //处理分钟
{
IPK_DateTime.IPK_Minute = tm[] -m_offset;
}
else
{
IPK_DateTime.IPK_Minute = tm[] -m_offset + ;
//此时小时跨度要再减一小时
IPK_DateTime.IPK_Hour = tm[]-(h_offset+); //减去快的6H and 跨时段1
} //格式化当前时间戳
snprintf(t_now, elcount(t_now),"%d/%d/%d %02d:%02d:%02d", tm[]+, tm[]+, tm[], IPK_DateTime.IPK_Hour,IPK_DateTime.IPK_Minute,tm[]);
}
//log输出 提示作用
on timer DateRemanderTimer
{
writeDbgLevel(, "CANoe Time: %s", t_canoe);
writeDbgLevel(, "Now Time:%s", t_now);
setTimer(DateRemanderTimer, varCycTime10);
} //激活IPK(当电源ON时,触发)
//去激活IPK(当电源OFF时,停发IPK报文)// 设置每次启动CANoe时,仪表盘的初值//电源模式变更时,处理IPK在总线上的活跃状态 //以下代码举两个例子描述,具体信号的变化,呈现在控制面板上的为物理值。其余信号可自己根据样板添加
//平均油耗 L/100Km
on envVar IPK_AverageFuelConsumption
{
int temp;
//偏移量与精度值
float factor = 0.01;
int offset = ; temp = getValue(IPK_AverageFuelConsumption); IPK_Data.IPK_AverageFuelConsumption = (temp-offset)/factor;
} //平均电耗 KWH/100km
on envVar IPK_AveragePowerConsumption
{
int temp;
//偏移量与精度值
float factor = 0.1;
int offset = -99.9;
temp = getValue(IPK_AveragePowerConsumption); IPK_Data.IPK_AveragePowerConsumption = (temp-offset)/factor;
}

5、车身模拟器,BCM+EMS

EMS仿真实现,发动机状态更新

 variables
{
char BCMStatusPanel[] = "BCM状态图";
char EMSCtrl[] = "发动机"; //本消息由EMS发出,包含引擎转速、加速踏板状态信号
message 0x334 EMS_EngineRPM; msTimer EMS_timer;
} on envVar PEPS_PowerMode
{
//获取电源模式 + 车速
varPowerMode = getValue(PEPS_PowerMode);
if(varPowerMode==)
{
putValue(EMS_EngStatus, );//stop
putValue(EMS_EngineSpeedRPM,);
InactiveEMS();
lastPowerMode = varPowerMode;
}
else
{
if(lastPowerMode== && varPowerMode==)
{
;
}
else
{
switch(varPowerMode)
{
case :
putValue(EMS_EngStatus, );//stop
putValue(EMS_EngineSpeedRPM,);
break;
case :
putValue(EMS_EngStatus, );//stop
putValue(EMS_EngineSpeedRPM,);
break;
case :
putValue(EMS_EngStatus, );
break;
case :
putValue(EMS_EngStatus, );//Cranking
putValue(EMS_EngineSpeedRPM,);
break;
default:
break;
}
ActiveEMS();
}
}
} //更新车身仿真器的状态
on envVar EMS_EngStatus
{
int temp;
temp = getValue(EMS_EngStatus);
EMS_EngineRPM.EMS_EngStatus = temp;
if(temp==)
{
IsEngineWorking = ; //发动机工作中
setPictureBoxImage(BCMStatusPanel, EMSCtrl, "..\\Panels\\picture\\启动中.bmp");
}
else if (temp==)//油车
{
IsEngineWorking = ;
setPictureBoxImage(BCMStatusPanel, EMSCtrl, "..\\Panels\\picture\\未启动.bmp");
}
else if(temp==) //PHEV
{
if(@GEEA1::varCarType == )
{
IsEngineWorking = ;
setPictureBoxImage(BCMStatusPanel, EMSCtrl, "..\\Panels\\picture\\未启动.bmp");
}
}
} //略去 EMS激活,去激活,初始值,报文发送的函数

BCM仿真器实现,四门六盖,锁状态

 variables
{
char BCMCtrlPanel[] = "ControlPanel";
char BCMStatusPanel[] = "BCM状态图";
char BCMHoodCtrl[]= "引擎盖";
char BCMTrunkCtrl[]= "后备箱";
char BCMLFDoorCtrl[]= "左前门";
char BCMLRDoorCtrl[]= "左后门";
char BCMRFDoorCtrl[]= "右前门";
char BCMRRDoorCtrl[]= "右后门";
char BCMSunroofCtrl[]= "天窗";
char BCMLockCtrl[]= "锁"; //本消息由BCM发出,包含BCM控制的各类开关以及加热器继电器开关信号
message 0x1 BCM_StateUpdate;
//左门窗
message 0x2 BCM_LDoorWindowState;
//右门窗
message 0x3 BCM_RDoorWindowState;
//本消息由BCM发出,包含前车窗状态及天窗状态信号
message 0x4 BCM_SunroofState;
// 发送100ms周期的报文
msTimer BCM_WndsDoors_timer; } on start
{
InitBCMValue();
InitBCMPanels();
//BCM不受PEPS电源模式影响,所以启动CANoe即可发出BCM报文
ActiveBCM();
} on timer BCM_WndsDoors_timer
{
output(BCM_SunroofState);
output(BCM_StateUpdate);
output(BCM_LDoorWindowState);
output(BCM_RDoorWindowState);
setTimer(BCM_WndsDoors_timer, varCycTime100);
} //设置每次启动CANoe时,BCM的初值
void InitBCMValue()
{
} void InitBCMPanels()
{
//打开控制面板 capl function, 此处不指明路径 直接遍历工程目录
openPanel(BCMCtrlPanel);
openPanel(BCMStatusPanel);
} //激活BCM往外发送报文
void ActiveBCM()
{
setTimer(BCM_WndsDoors_timer, varCycTime100);
} //停发BCM报文
void InactiveBCM()
{
cancelTimer(BCM_WndsDoors_timer);
} //预留一开关 停发所有报文
on envVar PEPS_PowerMode
{
varPowerMode = getValue(PEPS_PowerMode);
if(varPowerMode==) //CAN-Sleep
{
InactiveBCM();
lastPowerMode = varPowerMode;
}
else
{
if((varPowerMode==)&&(lastPowerMode==))
{
;
}
else if((==varPowerMode) || (==varPowerMode) || (==varPowerMode))
{
ActiveBCM(); //不是从3跳到0的模式,全激活
}
lastPowerMode = varPowerMode;
}
}
//天窗
on envVar BCM_SunroofAjarStatus
{
int temp; temp = getValue(BCM_SunroofAjarStatus);
writeDbgLevel(,"天窗信号=%d",temp);
BCM_SunroofState.L_Sunroof_Position=temp;
if(temp==) //未知
{
setPictureBoxImage(BCMStatusPanel,BCMSunroofCtrl,"..\\Panels\\picture\\天窗未知.bmp");
}
else if(temp==) //关闭
{
setPictureBoxImage(BCMStatusPanel,BCMSunroofCtrl,"..\\Panels\\picture\\天窗关闭.bmp");
}
else if(temp==) //开启
{
setPictureBoxImage(BCMStatusPanel,BCMSunroofCtrl,"..\\Panels\\picture\\天窗未关闭.bmp");
}
}
//驾驶位车窗
on envVar BCM_Drv_Wdw_PositionSts
{
int x,y; x = getvalue(BCM_Drv_Wdw_PositionSts);
y = getvalue(BCM_FrontLeftDoorAjarStatus);
writeDbgLevel(,"驾驶位车窗=%d",x);
BCM_LDoorWindowState.L_Drv_Wdw_PositionSts = x;
if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前窗开.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门和窗未关闭.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门关闭.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门未关闭.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前窗透气.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门未关闭窗透气.bmp");
}
}
//左后窗
on envVar BCM_RLD_Wdw_PositionSts
{
int x, y;
x = getValue(BCM_RLD_Wdw_PositionSts);
y = getValue(BCM_RearLeftDoorAjarStatus);
writeDbgLevel(,"左后车窗=%d",x);
BCM_LDoorWindowState.L_RLD_Wdw_PositionSts = x;
if((y==) && (x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后窗开.bmp");
}
else if((y==) && (x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门和窗未关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门未关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后窗透气.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门未关闭窗透气.bmp");
}
}
//左前门
on envVar BCM_FrontLeftDoorAjarStatus
{
int x, y, z;
x = getvalue(BCM_FrontLeftDoorAjarStatus);
y = getvalue(BCM_Drv_Wdw_PositionSts);
z = getValue(BCM_DoorLockStatusDrv);
BCM_LDoorWindowState.BCM_FrontLeftDoorAjarStatus = x;
if((x==) && (z==) && (varPowerMode==))
{ //防盗入侵报警,熄火OFF+锁车+开左前门触发,10s后恢复armed
putValue(BCM_ATWS_St,); //0x0: Armed0x1: Prearmed0x2: Disarmed0x3: Remind0x4: Alarm0x5: Partially Armed0x6: Not used0x7: Not used
BCM_StateUpdate.BCM_ATWS_St=getvalue(BCM_ATWS_St);
//setTimer(BCM_ATWS_timer, varCycTime10);
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前窗开.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门和窗未关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMLFDoorCtrl, "..\\Panels\\picture\\左前门未关闭.bmp");
}
}
//左后门
on envVar BCM_RearLeftDoorAjarStatus
{
int x,y;
x = getvalue(BCM_RearLeftDoorAjarStatus);
y = getvalue(BCM_RLD_Wdw_PositionSts);
BCM_LDoorWindowState.BCM_RearLeftDoorAjarStatus=x;
if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后窗开.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门和窗未关闭.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门关闭.bmp");
}
else if((x==)&&(y==))
{
setPictureBoxImage(BCMStatusPanel, BCMLRDoorCtrl, "..\\Panels\\picture\\左后门未关闭.bmp");
} }
//驾驶侧锁
on envVar BCM_DoorLockStatusDrv
{
int temp;
temp=getValue(BCM_DoorLockStatusDrv);
writeDbgLevel(,"门锁信号=%d",temp);
BCM_LDoorWindowState.BCM_DoorLockStatusDrv=temp;
if(temp==)
{
setPictureBoxImage(BCMStatusPanel,BCMLockCtrl,"..\\Panels\\picture\\锁开.bmp");
}
else
{
setPictureBoxImage(BCMStatusPanel,BCMLockCtrl,"..\\Panels\\picture\\锁闭.bmp");
}
}
//左后门锁
on envVar BCM_DoorLockStatusRL
{
BCM_LDoorWindowState.BCM_DoorLockStatusRL=getValue(BCM_DoorLockStatusRL);
}
//右前窗
on envVar BCM_Pas_Wdw_PositionSts
{
int x,y;
x = getvalue(BCM_Pas_Wdw_PositionSts);
y = getvalue(BCM_FrontRightDoorAjarStatus);
writeDbgLevel(,"副驾车窗=%d",x);
BCM_RDoorWindowState.L_Pas_Wdw_PositionSts=x;
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前窗开.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门和窗未关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门未关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前窗透气.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门未关闭窗透气.bmp");
}
}
//右后窗
on envVar BCM_RRD_Wdw_PositionSts
{
int x,y;
x = getvalue(BCM_RRD_Wdw_PositionSts);
y = getvalue(BCM_RearRightDoorAjarStatus); writeDbgLevel(,"右后车窗=%d",x);
BCM_RDoorWindowState.L_RRD_Wdw_PositionSts=x;
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后窗开.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门和窗未关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门未关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后窗透气.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门未关闭窗透气.bmp");
}
}
//副驾门锁
on envVar BCM_DoorLockStatusPassenger
{
BCM_RDoorWindowState.BCM_DoorLockStatusPass=getValue(BCM_DoorLockStatusPassenger);
}
//右后门锁
on envVar BCM_DoorLockStatusRR
{
BCM_RDoorWindowState.BCM_DoorLockStatusRR=getValue(BCM_DoorLockStatusRR);
}
//右前门
on envVar BCM_FrontRightDoorAjarStatus
{
int x,y;
x = getvalue(BCM_Pas_Wdw_PositionSts);;
y = getvalue(BCM_FrontRightDoorAjarStatus);
BCM_RDoorWindowState.BCM_FrontRightDoorAjarStatus=y;
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前窗开.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门和窗未关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门关闭.bmp");
}
else if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRFDoorCtrl, "..\\Panels\\picture\\右前门未关闭.bmp");
}
}
//右后门
on envVar BCM_RearRightDoorAjarStatus
{
int x, y;
y = getvalue(BCM_RearRightDoorAjarStatus);
x = getvalue(BCM_RRD_Wdw_PositionSts);
BCM_RDoorWindowState.BCM_RearRightDoorAjarStatus=y;
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后窗开.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门和窗未关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门关闭.bmp");
}
if((y==)&&(x==))
{
setPictureBoxImage(BCMStatusPanel, BCMRRDoorCtrl, "..\\Panels\\picture\\右后门未关闭.bmp");
}
} //一键关门
on envVar CloseDoors
{
int temp;
temp = getValue(CloseDoors);
if(temp==) //关闭
{
putValue(BCM_FrontLeftDoorAjarStatus,);
putValue(BCM_FrontRightDoorAjarStatus,);
putValue(BCM_RearLeftDoorAjarStatus,);
putValue(BCM_RearRightDoorAjarStatus,); }
else //开启
{
putValue(BCM_FrontLeftDoorAjarStatus,);
putValue(BCM_FrontRightDoorAjarStatus,);
putValue(BCM_RearLeftDoorAjarStatus,);
putValue(BCM_RearRightDoorAjarStatus,);
}
}
//一键关窗
on envVar CloseWnds
{
int temp;
temp = getValue(CloseWnds);
//writeDbgLevel(1,"一键关窗=%d",temp);
if(temp==) //关闭
{
putValue(BCM_Drv_Wdw_PositionSts,);
putValue(BCM_Pas_Wdw_PositionSts,);
putValue(BCM_RLD_Wdw_PositionSts,);
putValue(BCM_RRD_Wdw_PositionSts,); //天窗
putValue(BCM_SunroofAjarStatus, );
//开度值=0
putValue(BCM_Val_Wdw_Opened,);
}
else //全开
{
putValue(BCM_Drv_Wdw_PositionSts,);
putValue(BCM_Pas_Wdw_PositionSts,);
putValue(BCM_RLD_Wdw_PositionSts,);
putValue(BCM_RRD_Wdw_PositionSts,); //可屏蔽天窗
putValue(BCM_SunroofAjarStatus, );
//开度值=100
putValue(BCM_Val_Wdw_Opened,);
}
}
//一键锁止
on envVar LockDoors
{
int temp;
temp = getValue(LockDoors);
if(==temp)//锁
{
putValue(BCM_DoorLockStatusDrv,);
putValue(BCM_DoorLockStatusRL,);
putValue(BCM_DoorLockStatusPassenger,);
putValue(BCM_DoorLockStatusRR,);
setPictureBoxImage(BCMStatusPanel,BCMLockCtrl,"..\\Panels\\picture\\锁闭.bmp");
}
else //未锁
{
putValue(BCM_DoorLockStatusDrv,);
putValue(BCM_DoorLockStatusRL,);
putValue(BCM_DoorLockStatusPassenger,);
putValue(BCM_DoorLockStatusRR,);
setPictureBoxImage(BCMStatusPanel,BCMLockCtrl,"..\\Panels\\picture\\锁开.bmp");
}
} //防盗报警状态
on envVar BCM_ATWS_St
{
BCM_StateUpdate.BCM_ATWS_St = getValue(BCM_ATWS_St);
} //BCM信号提示后盖箱/后车门开启/关闭
on envVar BCM_TrunkAjarStatus
{
int temp; temp = getValue(BCM_TrunkAjarStatus);
BCM_StateUpdate.BCM_TrunkAjarStatus=temp;
if(temp==) //关闭
{
setPictureBoxImage(BCMStatusPanel,BCMTrunkCtrl,"..\\Panels\\picture\\后备箱关闭.bmp");
}
else{ //开启
setPictureBoxImage(BCMStatusPanel,BCMTrunkCtrl,"..\\Panels\\picture\\后备箱未关闭.bmp");
}
} //BCM信号提示引擎盖开启/关闭
on envVar BCM_HoodAjarStatus
{
int temp; temp = getValue(BCM_HoodAjarStatus);
BCM_StateUpdate.BCM_HoodAjarStatus=temp;
if(temp==)
{
setPictureBoxImage(BCMStatusPanel, BCMHoodCtrl, "..\\Panels\\picture\\引擎盖关闭.bmp");
}
else if(temp==)
{
setPictureBoxImage(BCMStatusPanel, BCMHoodCtrl, "..\\Panels\\picture\\引擎盖未关闭.bmp");
}
else
{
writeDbgLevel(, "预留值,无定义");
}
} on envVar BCM_Val_Wdw_Opened
{
int temp;
temp = getValue(BCM_Val_Wdw_Opened);
writeDbgLevel(, "窗户开度值=%d", temp);
BCM_RDoorWindowState.L_Pas_Val_Wdw_Opened=temp;
BCM_RDoorWindowState.L_RRD_Val_Wdw_Opened=temp;
BCM_LDoorWindowState.L_Drv_Val_Wdw_Opened=temp;
BCM_LDoorWindowState.L_RLD_Val_Wdw_Opened=temp;
}

6、PEPS仿真+T业务主逻辑实现

T业务,处理远程业务时,需根据产品的DBC中定义的报文去解读信号,判断业务逻辑。(示例代码中处理业务逻辑的信号解析规则属于随便举例,实际应用时根据DBC定义进行解读)

 variables
{
char log[];
float tFactor = 100000.0; //时间精度值
//本消息由PEPS发出, 鉴权
message 0x5 PEPS_TELChallengeCode;
//本消息由GW发送,包括PEPS的电源模式及报警信号等
message 0x6 GW_Info;
//由TBOX反馈的应答码
message 0x7 TBOX_ResponseCode; msTimer GW_PEPS_Timer;
//远程控制的PEPS响应定时器
msTimer GW_PEPS_TimerRespSuccess;
//接收到报文的定时器
msTimer GW_PEPS_TimerRespReceived;
//PEPS启动认证码的定时器,只发三帧
msTimer GW_PEPS_TimerSendChallengeCode;
//远程启动定时器
timer RespRmtEngineWorkingTimer; //PM 净化定时器
timer RmtAirCleanTimer;
//加热定时器
timer RmtDrvHeatTimer;
timer RmtPasHeatTimer;
//空调允许时间
timer RmtACOnTimer;
//
timer UpdateStatusTimer; //以下内容属于PEPS加密算法的内容
const dword SC32 = 0x00112233;
const qword CC64 = 0x94254547464A5B7DLL; //后最LL
//SK128 无法获取; RN32 每次鉴权随机生成;
dword RN32;
//设别远程启动的类型;在发生鉴权行为的时候会使用到
//1=启动;2=熄火;3=使能;4=禁止;0 预留
int rmtReqTpye=;
//是否已认真过
int IsAuthed = ; //远程控制请求
byte rmtCtrlReq;
// 远程查询PM2.5
byte rmtReqPM;
//远程查询
byte rmtReqQ;
//远程启动
byte rmtStartReq;
byte rmtStopReq;
//远程加热座椅
byte rmtHeatDrvSeat;
byte rmtHeatPasSeat; //远程开关空调
byte rmtACReq; //byte(3) 温度值+空调的启停状态
byte acReq;
byte acTemp; //远程空气净化
byte rmtAirCleanerReq; //发动机运行时长 分级
byte rmtEngWorkTimeLvl = ;
int rmtEngWorkTime = ; //远程发动机启动时长, 单位s
//延时
byte rmtEngWorkDelayTimeLvl = ;
//远程运行时间
int rmtWorkTime = ; //包含启动空调,加热,绿净的时长
int tempWorkTime; //临时变量 存放rmtworkTime
//远程禁允发动机
byte rmtForbidEngReq;
byte rmtPermitEngReq; //记忆PEPS应答报文的发送次数
int pepsRespCnt=;
//记忆PEPS认证报文的发送次数
int pepsAuthCnt=;
//
int pepsRecvCnt=;
} on start
{
InitLogging();
InitPEPSValue();
ActivePEPS(); } //关闭CANoe时 停止记录LOG
on stopMeasurement
{
writeDestroy(mTrace);
} //标记当前电源模式, 3=休眠开关, KL15电源通断可使用VT板卡实现,否则手动操作实现为半自动化方案
on envVar PEPS_PowerMode
{
varPowerMode = getValue(PEPS_PowerMode);
//刷新报文值
GW_Info.PEPS_PowerMode = varPowerMode;
if(varPowerMode==)
{
InactivePEPS();
lastPowerMode = varPowerMode;
}
else
{
if((varPowerMode==)&&(lastPowerMode==))
{
;
}
else if((==varPowerMode) || (==varPowerMode) || (==varPowerMode))
{
ActivePEPS(); //不是从3跳到0的模式,全激活
}
lastPowerMode = varPowerMode;
}
} //标记PEPS应答的错误码
on envVar PEPS_FailReason2TBOX
{
int temp;
temp = getValue(PEPS_FailReason2TBOX);
writeDbgLevel(,"PEPS_FailReason2TBOX=0x%x",temp);
GW_Info.PEPS_FailReason2TBOX = temp;
}
//标记PEPS应答的成功与否
on envVar PEPS_StatusResponse2TBOX
{
GW_Info.PEPS_StatusResponse2TBOX = getValue(PEPS_StatusResponse2TBOX);
}
//标记发动机的启动模式
on envVar PEPS_RemoteControlSt
{
GW_Info.PEPS_RemoteControlSt=getValue(PEPS_RemoteControlSt);
} /***********************************************************
* description : TBOX响应T业务的请求的报文
* parameter : None
* creation date: 2018/10/17
* author : xxx
* revision date:
* revision log :
* modifier :
***********************************************************/
on message TBOX_RmtCtrlInfo //此消息由DBC中定义的远程控制报文可获取, 具体的报文解析,字节信号位等由DBC定义
{
rmtReqQ = (this.byte() & 0x03);
//远程控制 + 查询类
rmtCtrlReq = this.TBOX_DoorsLock;
rmtReqPM = this.TBOX_PM25; //远程发动机延时等级
rmtEngWorkDelayTimeLvl = this.TBOX_EngineDelayTime;
rmtStartReq = this.TBOX_EngineStartReq;
rmtStopReq = this.TBOX_EngineStopReq;
rmtACReq = this.byte();
rmtAirCleanerReq = this.TBOX_AirCleanerReq;
rmtForbidEngReq = this.TBOX_EngineForbidReq;
rmtPermitEngReq = this.TBOX_EnginePermitReq;
//PEPSRespReceived(); //只要一接到接收指令 立即回复处理中
//远程控制
RespRmtCtrlCmd(rmtCtrlReq);
if( != rmtEngWorkDelayTimeLvl) //如果不等于0, 有控制请求
{
PEPSRespReceived();
switch(rmtEngWorkDelayTimeLvl)
{
case 0x1://1min
rmtWorkTime = ;
break;
case 0x2://3min
rmtWorkTime = ;
break;
case 0x3://5min
rmtWorkTime = ;
break;
case 0x4://10min
rmtWorkTime = ;
break;
default:
break;
}
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,发动机运行时间延长%d", timeNow()/tFactor, rmtWorkTime);
writeLineEx(mTrace, INFO, log);
PEPSRespSuccess();
}
//远程查询
if( == rmtReqPM)
{
PEPSRespReceived();
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,PM2.5查询", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
putValue(AC_PM25Sts, );//complate
PEPSRespSuccess();
}
if( == rmtReqQ)
{
PEPSRespReceived();
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,查询天窗车窗", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
PEPSRespSuccess();
}
//远程启动
if( == rmtStartReq)
{
PEPSRespReceived();
rmtReqTpye = ;
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,启动发动机", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
rmtEngWorkTimeLvl = this.TBOX_EngineStartTime;
switch(rmtEngWorkTimeLvl)
{
case 0x1://1min
rmtEngWorkTime = ;
break;
case 0x2://3min
rmtEngWorkTime = ;
break;
case 0x3://5min
rmtEngWorkTime = ;
break;
case 0x4://10min
rmtEngWorkTime = ;
break;
default:
break;
}
snprintf(log, elcount(log),"%f <- TBOX, 启动发动机时长%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
PEPSReqAuth();
} //远程停止
if(rmtStopReq==)
{
rmtReqTpye = ;
PEPSRespReceived(); snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,停止发动机", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); PEPSRespSuccess();
RespRmtStop();
}
//远程禁止发动机
if( == rmtForbidEngReq)
{
rmtReqTpye = ;
PEPSRespReceived();
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,禁止启动", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); PEPSReqAuth();
}
//远程使能发动机
if( == rmtPermitEngReq)
{
rmtReqTpye = ;
PEPSRespReceived();
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,允许启动", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); PEPSReqAuth();
}
//PM 净化
if(==rmtAirCleanerReq) //2=ON , 1=OFF, 0=No Req
{
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,空气净化开启", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); PEPSRespReceived();
PEPSRespSuccess();
RespRmtOpenAirClean(); //区分
}
else if(==rmtAirCleanerReq)
{
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令,空气净化关闭", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); PEPSRespReceived();
PEPSRespSuccess();
RespRmtCloseAirClean();
} //远程空调
if(0x1F != rmtACReq) //2=ON , 1=OFF, 0=No Req
{
PEPSRespReceived();
acReq = ((rmtACReq >> ) & 0x3);
acTemp = (rmtACReq & 0x1F);
snprintf(log, elcount(log),"%f <- TBOX, 接收到指令, 操作空调req=%d, temp=%d", timeNow()/tFactor,acReq,acTemp);
writeLineEx(mTrace, INFO, log); PEPSRespSuccess();
if(==acReq)
{
RespRmtOpenAC(); //open
}
else if(==acReq)
{
RespRmtCloseAC(); //close
}
}
if(this.byte()!=0xE7)
{
rmtHeatDrvSeat = this.TBOX_DrvHeatReq;
rmtHeatPasSeat = this.TBOX_PassHeatReq; snprintf(log, elcount(log),"%f <- TBOX, 接收到指令, 座椅加热Drv=%d,Pas=%d", timeNow()/tFactor,rmtHeatDrvSeat, rmtHeatPasSeat);
writeLineEx(mTrace, INFO, log); PEPSRespReceived();
PEPSRespSuccess();
//主驾
switch(rmtHeatDrvSeat)
{
case 0x0:
RespRmtCloseDrvHeat();
break;
case 0x1:
RespRmtOpenDrvHeat(rmtHeatDrvSeat);
break;
case 0x2:
RespRmtOpenDrvHeat(rmtHeatDrvSeat);
break;
default:
break;
} //副驾
switch(rmtHeatPasSeat)
{
case 0x0:
RespRmtClosePasHeat();
break;
case 0x1:
RespRmtOpenPasHeat(rmtHeatPasSeat);
break;
case 0x2:
RespRmtOpenPasHeat(rmtHeatPasSeat);
break;
default:
break;
}
}
} //TBOX响应PEPS挑战码,发出的认证码
on message TBOX_ResponseCode
{
snprintf(log, elcount(log),"%f <- TBOX, response peps auth request", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
//PEPS回复控制成功通过
PEPSRespSuccess();
if(rmtReqTpye == ){
RespRmtStart();
//设置发动机的运行模式与运行时长,启动定时器
snprintf(log, elcount(log),"%f <- TBOX, engine work time=%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
setTimer(RespRmtEngineWorkingTimer, rmtEngWorkTime);
}
else if(rmtReqTpye == ) //实测关闭发动机不需要鉴权
{
RespRmtStop();
}
else if( == rmtReqTpye) //使能
{
RespRmtPermit();
}
else if( == rmtReqTpye) //禁止
{
RespRmtForbidden();
}
} //远程启动发动机运行时长定时器
on timer RespRmtEngineWorkingTimer
{
//发动机置位远程启动模式
putValue(PEPS_RemoteControlSt,);
//电源置为ON
putValue(PEPS_PowerMode, );
//发动机置位running
putValue(EMS_EngStatus,);
} //空调开启一段时间后 更新温度传感器信号
on timer UpdateStatusTimer
{
; //未实现
} //空调运行时长定时器
on timer RmtACOnTimer
{
putValue(AC_OnState, );
} //空气净化运行时长定时器
on timer RmtAirCleanTimer
{
putValue(AC_AirCleanState, );
putValue(AC_OnState,);
} //主驾加热运行时长定时器
on timer RmtDrvHeatTimer
{
putValue(HVSM_DrvHeatSts, );
} //副驾加热运行时长定时器
on timer RmtPasHeatTimer
{
putValue(HVSM_PassHeatSts,);
} //响应远程加热主驾座椅
void RespRmtOpenDrvHeat(int level)
{
putValue(HVSM_DrvHeatSts,level);
if((==IsEngineWorking) && (rmtWorkTime!=))
{
snprintf(log, elcount(log),"%f -> HVSM, 先启动发动机,再开启座椅加热,延长发动机运行时长%d", timeNow()/tFactor, rmtWorkTime);
writeLineEx(mTrace, INFO, log);
tempWorkTime = rmtWorkTime;
cancelTimer(RespRmtEngineWorkingTimer); //取消原定时器
setTimer(RespRmtEngineWorkingTimer, tempWorkTime); //重置发动机定时器
setTimer(RmtDrvHeatTimer, tempWorkTime);
rmtWorkTime=;
}
else
{
snprintf(log, elcount(log),"%f -> HVSM, 直接座椅加热,发动机运行时长%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
setTimer(RmtDrvHeatTimer, rmtEngWorkTime);
}
} //响应远程加热副驾座椅
void RespRmtOpenPasHeat(int level)
{
putValue(HVSM_PassHeatSts,level);
if((==IsEngineWorking) && (rmtWorkTime!=))
{
snprintf(log, elcount(log),"%f -> HVSM, 先启动发动机,再加热座椅,延长发动机运行时长%d", timeNow()/tFactor, rmtWorkTime);
writeLineEx(mTrace, INFO, log);
tempWorkTime = rmtWorkTime;
cancelTimer(RespRmtEngineWorkingTimer); //取消原定时器
setTimer(RespRmtEngineWorkingTimer, tempWorkTime); //重置发动机定时器
setTimer(RmtPasHeatTimer, tempWorkTime);
rmtWorkTime=; //用完后归零
}
else
{
snprintf(log, elcount(log),"%f -> HVSM, 直接加热座椅,发动机运行时长%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
setTimer(RmtPasHeatTimer, rmtEngWorkTime);
}
} //响应关闭座椅加热
void RespRmtCloseDrvHeat()
{
cancelTimer(RmtDrvHeatTimer);
putValue(HVSM_DrvHeatSts, );
} //响应关闭座椅加热
void RespRmtClosePasHeat()
{
cancelTimer(RmtPasHeatTimer);
putValue(HVSM_PassHeatSts, );
} //响应开启空调
void RespRmtOpenAC()
{
putValue(AC_OnState, );
if((==IsEngineWorking) && (rmtWorkTime!=))
{
//先启动发动机 后启动空调
snprintf(log, elcount(log),"%f -> AC, 先启动发动机,再开启空调,延长发动机运行时长%d", timeNow()/tFactor, rmtWorkTime);
writeLineEx(mTrace, INFO, log);
tempWorkTime = rmtWorkTime;
cancelTimer(RespRmtEngineWorkingTimer); //取消原定时器
setTimer(RespRmtEngineWorkingTimer, tempWorkTime); //重置发动机定时器
setTimer(RmtACOnTimer, tempWorkTime);
rmtWorkTime=; //用完后归零
}
else
{
//直接启动空调
snprintf(log, elcount(log),"%f -> AC, 直接启动空调,发动机运行时长%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
setTimer(RmtACOnTimer, rmtEngWorkTime);
}
} //响应关闭空调
void RespRmtCloseAC()
{
cancelTimer(RmtACOnTimer);
putValue(AC_OnState, );
} //响应关闭空气净化
void RespRmtCloseAirClean()
{
cancelTimer(RmtAirCleanTimer);
putValue(AC_AirCleanState, );
putValue(AC_OnState, ); } //响应开启PM净化
void RespRmtOpenAirClean()
{
putValue(AC_OnState, );
putValue(AC_AirCleanState, );
if((==IsEngineWorking) && (rmtWorkTime!=))
{
//先启动发动机 后启动PM净化
snprintf(log, elcount(log),"%f -> AC, 先启动发动机 再开空气净化,延长发动机运行时间%d", timeNow()/tFactor, rmtWorkTime);
writeLineEx(mTrace, INFO, log);
tempWorkTime = rmtWorkTime;
cancelTimer(RespRmtEngineWorkingTimer); //取消原定时器
setTimer(RespRmtEngineWorkingTimer, rmtWorkTime); //重置发动机定时器
setTimer(RmtAirCleanTimer, rmtWorkTime); //置位空气净化定时器
rmtWorkTime = ;
}
else
{
//直接启动PM净化
snprintf(log, elcount(log),"%f -> AC, 直接开启空气净化,发动机运行时间%d", timeNow()/tFactor, rmtEngWorkTime);
writeLineEx(mTrace, INFO, log);
setTimer(RmtAirCleanTimer, rmtEngWorkTime); //置位空气净化定时器
} } //响应远程控制指令
void RespRmtCtrlCmd(int cmd)
{
//判断远程控制类逻辑
switch(cmd){
case 0x0://No command
break;
case 0x1://All door lock
snprintf(log, elcount(log),"%f <- TBOX, 接收上锁指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetDoorsLocked();
PEPSRespSuccess();
break;
case 0x2://Blink lamp 闪灯
break; //APP未实现单独指令
case 0x3://All door unlock
snprintf(log, elcount(log),"%f <- TBOX, 接收解锁指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetDoorsUnlocked();
PEPSRespSuccess();
break;
case 0x4://Whistle 鸣笛
break; //APP未实现单独指令
case 0x5://Global closing-window up
snprintf(log, elcount(log),"%f <- TBOX, 接收关窗指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetWndsClosed();
PEPSRespSuccess();
break;
case 0x6://Closing window
break; //APP未实现单独指令
case 0x7: //Closing sunroof
break; //APP未实现单独指令
case 0x8://Global opening-window down
snprintf(log, elcount(log),"%f <- TBOX, 接收开窗指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetWndsOpened();
PEPSRespSuccess();
break;
case 0x9://Vehicle search
snprintf(log, elcount(log),"%f <- TBOX, 接收寻车指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
PEPSRespSuccess();
break;
case 0xA://Trunk unlock
snprintf(log, elcount(log),"%f <- TBOX, 接收开启后备箱指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetTrunkOpened();
PEPSRespSuccess();
break;
case 0xB://Window ventilate
snprintf(log, elcount(log),"%f <- TBOX, 接收透气指令", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
SetWndsVentilate();
PEPSRespSuccess();
break;
case 0xC://Opening sunroof
break; //APP未实现单独指令
default://others are reserved
break; //预留指令
}
} //远程禁止启动发动机
void RespRmtForbidden()
{
GW_Info.PEPS_EngineforbidSt=;
} //远程允许启动发动机
void RespRmtPermit()
{
GW_Info.PEPS_EngineforbidSt=;
} //远程停止发动机
void RespRmtStop()
{
cancelTimer(RespRmtEngineWorkingTimer); putValue(PEPS_RemoteControlSt,);
putValue(EMS_EngStatus, );
putValue(PEPS_PowerMode, );
//停止发动机后 所有控制器开的状态都归零
putValue(AC_AirCleanState, );
putValue(AC_OnState,);
putValue(HVSM_DrvHeatSts, );
putValue(HVSM_PassHeatSts, );
} //响应远程启动
void RespRmtStart()
{
IsAuthed =;
putValue(EMS_EngStatus, ); //发动机置位远程启动模式
putValue(PEPS_RemoteControlSt,);
//发动机置位running
putValue(EMS_EngStatus, );
//电源置为ON
putValue(PEPS_PowerMode, );
IsEngineWorking = ; //标记发动机已经工作中, Delay时间不会发出
snprintf(log, elcount(log),"%f <- EMS PEPS, Engine running, Power on", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
//TestWaitForTimeout(3000);//延时函数在纯CAPL程序中不能使用
} //初始化电源模式
void InitPEPSValue()
{
putValue(PEPS_PowerMode, );
putValue(PEPS_StatusResponse2TBOX,);
putValue(PEPS_FailReason2TBOX,);
putValue(PEPS_RemoteControlSt,);
GW_Info.PEPS_PowerModeValidity = ;
GW_Info.PEPS_EngineforbidSt=; //发动机允许远程使能状态
} void ActivePEPS()
{
setTImer(GW_PEPS_Timer, varCycTime100);
} void InactivePEPS()
{
cancelTimer(GW_PEPS_Timer);
} //每次鉴权生成随机认证码
void GenerateRN32()
{
RN32 = random(0xFFFFFFFF);
} //窗户透气响应
void SetWndsVentilate()
{
//窗户开
putValue(BCM_Drv_Wdw_PositionSts,);
putValue(BCM_RLD_Wdw_PositionSts,);
putValue(BCM_Pas_Wdw_PositionSts,);
putValue(BCM_RRD_Wdw_PositionSts,);
putValue(BCM_Val_Wdw_Opened, ); //开度值20%
} //响应远程关闭车窗指令
void SetWndsClosed()
{
// putValue(CloseWnds,0);
putValue(BCM_Drv_Wdw_PositionSts,);
putValue(BCM_RLD_Wdw_PositionSts,);
putValue(BCM_Pas_Wdw_PositionSts,);
putValue(BCM_RRD_Wdw_PositionSts,);
putValue(BCM_Val_Wdw_Opened, );
} //响应远程上锁指令
void SetDoorsLocked()
{
putValue(LockDoors,);
} //响应远程解锁指令
void SetDoorsUnlocked()
{
putValue(LockDoors,);
} //响应远程开窗指令
void SetWndsOpened()
{
// putValue(CloseWnds,1);//开启
putValue(BCM_Drv_Wdw_PositionSts,);
putValue(BCM_RLD_Wdw_PositionSts,);
putValue(BCM_Pas_Wdw_PositionSts,);
putValue(BCM_RRD_Wdw_PositionSts,);
putValue(BCM_Val_Wdw_Opened, );
} //响应远程打开后备箱
void SetTrunkOpened()
{
putValue(BCM_TrunkAjarStatus, ); //开启后备箱
} //PEPS回复控制成功报文
void PEPSRespSuccess()
{
setTimer(GW_PEPS_TimerRespSuccess, varCycTime100);
} void PEPSRespReceived()
{
// setTimer(GW_PEPS_TimerRespReceived, varCycTime20);
} //PEPS发起认证请求
void PEPSReqAuth()
{
//算法不实现!
//若实际环境中接入了PEPS设备,则需实车抓取报文使用固定一组报文访问
//安全隐患,PEPS入侵(可能设计:每次随机的挑战码,携带当前时间,设置计数器和校验位验证有效性,限定时间内的重复报文无效)
if(rmtReqTpye ==)
{
GenerateChallengeCode4Start();
}
else if(rmtReqTpye ==)
{
GenerateChallengeCode4Stop();
} setTimer(GW_PEPS_TimerSendChallengeCode, varCycTime20);
IsAuthed = ; //已经发起过鉴权
} void GenerateChallengeCode4Stop()
{
//关闭发动机鉴权
}
//生成PEPS挑战码,启动发动机鉴权
void GenerateChallengeCode4Start()
{
if(pepsAuthCnt==)
{
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte0 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte1 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte2 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte3 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte4 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte5 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte6 = random(0xff);
PEPS_TELChallengeCode.PEPS_TELchallengeCode_Byte7 = random(0xff);
}
} //TBOX预约充电请求(电动车)/
on message TBOX_ReservationChgSet
{
//线束不支持双路CAN 暂且无法实现
} //TBOX被唤醒的原因指示
on message NWM_TBOX_Information
{
int reasons;
reasons = this.TBOX_Wakeup_reasons;
switch(reasons){
case 0x2:
writeLineEx(mTrace, , "NM PDU Received");
break;
case 0x5:
writeLineEx(mTrace, , "KL15 On");
break;
case 0x06:
writeLineEx(mTrace, , "Telematics service");
break;
default:
break;
}
} //PEPS周期报文发送
on timer GW_PEPS_Timer
{
output(GW_Info);
setTimer(GW_PEPS_Timer, varCycTime100);
} //响应远程请求,PEPS反馈Received
on timer GW_PEPS_TimerRespReceived
{
pepsRecvCnt+=;
if(pepsRecvCnt<)
{
snprintf(log, elcount(log),"%f -> PEPS, response 'in progress'", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
putValue(PEPS_FailReason2TBOX, );
putValue(PEPS_StatusResponse2TBOX, );
setTimer(GW_PEPS_TimerRespReceived, varCycTime20); //200ms一帧
}
else
{
cancelTimer(GW_PEPS_TimerRespReceived);
putValue(PEPS_FailReason2TBOX, );
putValue(PEPS_StatusResponse2TBOX, );
pepsRecvCnt = ;
}
} //响应远程请求,PEPS反馈Success
on timer GW_PEPS_TimerRespSuccess
{
pepsRespCnt+=;
if(pepsRespCnt<)
{
snprintf(log, elcount(log),"%f -> PEPS, response 'success'", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log);
putValue(PEPS_FailReason2TBOX, );
putValue(PEPS_StatusResponse2TBOX, );
setTimer(GW_PEPS_TimerRespSuccess, varCycTime100);
}
else
{
cancelTimer(GW_PEPS_TimerRespSuccess);
putValue(PEPS_FailReason2TBOX, );
putValue(PEPS_StatusResponse2TBOX, );
pepsRespCnt = ;
}
} //响应远程启动请求, PEPS发出挑战码
on timer GW_PEPS_TimerSendChallengeCode
{ pepsAuthCnt+=;
if(pepsAuthCnt<)
{
snprintf(log, elcount(log),"%f -> PEPS, send challenge code", timeNow()/tFactor);
writeLineEx(mTrace, INFO, log); output(PEPS_TELChallengeCode);
setTimer(GW_PEPS_TimerSendChallengeCode, varCycTime20);//递归 发三帧,20ms一帧
}
else
{
cancelTimer(GW_PEPS_TimerSendChallengeCode);
pepsAuthCnt = ;
}
}

7、效果展示

整个车辆网功能中涉及到整车中其它ECU相关的业务,也可参照以上实现逻辑去进行仿真。此处不一一举例。

我在台架中接入真实仪表验证我的仿真逻辑(节点支持热增减,没有ECU则使用仿真节点,有真实ECU则屏蔽)。

启动仿真程序后,操作控制器,验证车身仿真器的实现,可以检验出仿真代码实现的正确性

--------------------------以上T业务自动化仿真测试台架全内容完结-----------------------------

05-11 22:33