CANoe 使用CAPL计算报文周期,检测报文是否掉线

在网络管理相关的测试中,无论是对于OESK还是AutoSar网管,都需要检测网络管理报文有没有停发,总线上的节点是否掉线等。

这里实现了一段简单的代码用来检测总线上的报文周期(此段代码仅适用于周期为固定周期的情况,无法计算AutoSar中报文快发周期与Nos下的正常网管周期)

思路讲解:

在总线上收到同一个ID的第一帧CAN帧时,记录下此刻的时间节点t1,等待收到第二帧时,记录下此刻的时间节点t2,继续等待收到第三帧时,记录下此刻的时间t3。

令z1 = t2-t1;

令z2 = t3-t2;

如果z2与z1的比值在一定范围内,便将(z1+z2)/2,记作此报文的周期,若不在此范围内,认为该三帧之间的间隔时间相差太大,不是连续报文,不记录周期,继续接收3帧报文,直到收到连续的三帧,计算出周期T。

如果将比值的范围设定为0.95~1.05之间,那么只要z1与z2的差距不超过10%,就视为两个差值有效,将二者求和之后取平均值即可计算出一个相对来说准确的周期(如果报文的周期都是整百的,可以将此周期值取整,找到最近的整百)。

随后在需要检测报文丢失的地方,取出当前的时间t4,使用t4 - 报文上一次出现时的时间,如果这个时间大于周期的一定的倍数,则认为这个报文已经丢失了。

实现过程:

variables
{
  struct CANMessage {
    word NmFrID;
    dword NmFrLastTime;//上次出现的时间,单位ms
    dword NmFrCycTime;//周期
    byte NmIdExistsFlag;//是否已存在标志位
};
  
  struct CANMessage gNmFrArray[256];//声明NM结构体数组变量,0x500~0x5ff共256帧
  
  enum _bool{
    _false,
    _true
  };
  
  dword t1[256],t2[256],t3[256];//三个时间数组,用于存放周期相关的时间
  
}

enum _bool is_message_timed_out(struct CANMessage Msg) //检测CAN报文是否已经超时
{
  dword current_time;//获取当前的时间
  current_time = (timeNow() / 100);
  current_time = current_time - Msg.NmFrLastTime;//得到当前时间和报文上一次出现的时间差
  
  if(current_time > 5 * Msg.NmFrCycTime)
  {
    return _true;
  }
  else
  {
    return _false;
  }
}


byte check_for_timed_out_messages(word id)//此函数可以传入指定的ID,返回当前指定ID的离线状态。若已离线返回1,在线返回0
{
  byte index;
  index = id - 0x500;
  
  if(gNmFrArray[index].NmIdExistsFlag && is_message_timed_out(gNmFrArray[index]))//如果报文已经出现过,并且报文超时了
  {
    gNmFrArray[index].NmIdExistsFlag = _false;
    return _true;
  }
  else
  {
    return _false;
  }
}

void CelargVar()
{
  int i;
  for ( i = 0; i < elcount(gNmFrArray); i++) //清空结构体
  {
    gNmFrArray[i].NmIdExistsFlag = _false;
    gNmFrArray[i].NmFrID = 0;
    gNmFrArray[i].NmFrCycTime = 0;
    gNmFrArray[i].NmFrLastTime = 0;
  }
}

on start
{
  CelargVar();
}

on key'A'
{
  //check_for_timed_out_messages();
  int i;
  for(i=0;i<elcount(gNmFrArray);i++)
  {
    if(check_for_timed_out_messages(i + 0x500))
    {
      write("CAN ID = 0x%lx 已经离线",i+0x500);
    }
  }
  
}

on message 0x500-0x5ff
{ 
  byte Address;
  
  Address = this.id - 0x500;//把ID转化为数组索引
  if(gNmFrArray[Address].NmIdExistsFlag == _true)//如果已经出现了2次此报文
  {
    double tempZ;
    tempZ = 0;
    t1[Address] = (timeNow() / 100);//计算出当前的时间
    t2[Address] = abs(t1[Address] - gNmFrArray[Address].NmFrLastTime);
    
    tempZ = (double)t3[Address] / (double)t2[Address];
    
    write("tempZ = %lf",tempZ);
    
    //如果是第一次执行到这里,t3-t2是无法满足条件的,即要第二次执行到这个代码,也就是收到了三帧,才会生效
    if(tempZ >= 0.95 && tempZ <= 1.05 && gNmFrArray[Address].NmFrCycTime == 0)//赋值一次周期之后,周期有了数值,条件也不成立了,即只更新一次周期
    {
      if(t3[Address] != 0)
      {
        gNmFrArray[Address].NmFrCycTime = (t3[Address] + t2[Address]) / 2;//计算周期
        write("报文0x%LX的周期是%d ms",this.id,gNmFrArray[Address].NmFrCycTime);
      }
    }   
    t3[Address] = t2[Address];
  }

  
  if (!gNmFrArray[Address].NmIdExistsFlag || gNmFrArray[Address].NmFrID == this.id) //ID没有存储过,或者传过来的ID已经存储了,则更新数据
  {
      gNmFrArray[Address].NmFrID = this.id;
      gNmFrArray[Address].NmFrLastTime = (timeNow() / 100);//取出时间
      gNmFrArray[Address].NmIdExistsFlag = _true;
      //write("更新ID = %LX信息",id);
  }
  
  
  
  //add_or_update_message(this.id);
}

接着在IG上添加几个报文测试下周期检测准不准。

可以看到,检测到周期和IG中的发送周期一致。

如果我改为手动发送,制造较大的报文间隔,周期将不会检测成功

当z1与z2相差过大时,不会计算周期

当z1与z2的比值在给定范围内时,求出周期。

接着我们看看检测报文丢失的功能。

此时,我停掉前两个报文,过会儿后按下按键A查询报文丢失情况

成功检测到了离线的报文。

当然,这里是用按键来触发检测的,实际使用中我们应该要知道报文的实时状态,可以略微修改一下代码将检测挪到on message中去检查或者开个定时器来检查,这里就不再演示了。如果有需要的话,可以评论区或者后台联系我,我这里有完善之后的源码,适用于OESK和AutoSar网络管理(可以检测快慢周期)。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值