手撸CAPL(二)——OSEK网络管理N个节点建环机制(包含节点建环/丢失/跳过等)代码自取

  • 前言
  • 确认模拟节点
  • 发送机制
  • 节点排序
  • 节点加入并建环
  • 节点丢失
  • 节点跳过
  • 测试例子
  • 总结
    ————————————————
    闲暇之际,听到不知名网友呼我关于OSEK的建环机制用CAPL怎么做?
    我会的有以下三种:
  1. 利用.dll调用,这得看函数解析,对新手不太友好。

  2. 建立network node仿真节点,每个仿真节点都有一个完整的OSEK机制,即相互独立运作建环,睡眠唤醒等;编写测试用例简单;这得有开发基础及逻辑清晰的头脑,再花费亿点点时间(小七用的这种)。

  3. 只满足基本的建环与丢失,代码量少,需要迎合测试用例(推荐)。

今天讲第3种。
在这里插入图片描述
新建工程,Insert CAPL Test Module > osek.can

确认模拟节点
  • 以0x604为ECU节点,0x601,0x605,0x603,0x650,0x67F为模拟节点。
variables{
  dword   ecu_node_id = 0x604;//ECU节点
  dword   node_id[5] = {0x601,0x605,0x603,0x650,0x67f};//模拟节点5个
  }
发送机制
  • 初步模拟出ECU节点运行,直到跛行状态持续
variables{
  message 0x001 g_Tx = {dlc=0x08};
  dword   ecu_node_cycle = 0;//周期
  msTimer TsingleNode;//模拟osek管理发送机制
  int     rxLimit_sum = 4,rxLimit_n = 0;
}
on timer TsingleNode//模拟ECU节点的定时器
{
    if(ecu_node_cycle == 0)
    {
        ecu_node_cycle = 100;
        g_Tx.byte(1) = 0x01;
    }
    else if(ecu_node_cycle == 100)
    {
        ecu_node_cycle = 260;
        g_Tx.byte(1) = 0x02;
    }
    else if(ecu_node_cycle == 260 || ecu_node_cycle == 1000)
    {
        rxLimit_n++;
        if(rxLimit_n >= rxLimit_sum + 1)
        {
            ecu_node_cycle = 1000;
            g_Tx.byte(1) = 0x04;
        }
        else
        {
            if(rxLimit_n == rxLimit_sum)
            {
                ecu_node_cycle = 1000;
                g_Tx.byte(1) = 0x01;
            }
            else
            {
                ecu_node_cycle = 100;
                g_Tx.byte(1) = 0x01;
            }
        }
    }
    g_Tx.id = ecu_node_id;
    g_Tx.byte(0) = ecu_node_id - node_id_min;
    output(g_Tx);
    setTimer(TsingleNode,ecu_node_cycle);
}
  • 效果
    在这里插入图片描述
节点排序
  • 声明变量数组按ID大小,从小到大存储当前模拟节点及ECU 节点
void alignNodes(byte sim_node_n, byte node_behavior)//排列节点id大小,只有加入和丢失两种操作。
{
    dword value;
    int Tempi,Tempj,Tempk;
    if(node_behavior != gSim_on && node_behavior != gSim_out)//加入和丢失两种操作
      return;
    if(sim_node_n != 0xFF)//单个节点
    {
        if(node_behavior == gSim_on)//模拟节点加入,要重新排列发送顺序
        {
            for (Tempi = 0; Tempi < cur_node_id_sum; Tempi++)
            {
                if(cur_node_id[Tempi] == node_id[sim_node_n - 1])//加入时,要排除当前节点数组是否有待加入的ID
                  return;
            }
//            writeEx(-3,0,"0x%x节点加入",node_id[sim_node_n-1]);
            cur_node_id[cur_node_id_sum++] = node_id[sim_node_n - 1];
            if(cur_node_id_sum == 1)//有一个节点时就先增加进去,防止每次增加节点时都增加一个ECU ID
              cur_node_id[cur_node_id_sum++] = ecu_node_id;//获取当前所有节点ID
        }
        else if(node_behavior == gSim_out)//丢失,要重新排列发送顺序
        {
            for (Tempi = 0; Tempi < cur_node_id_sum; Tempi++)
            {
                if(cur_node_id[Tempi] == node_id[sim_node_n - 1])//丢失时判断,找到要丢失模拟节点在cur_node_id里位置
                {
//                    writeEx(-3,0,"0x%x节点丢失",node_id[sim_node_n-1]);
                    for(Tempj = Tempi+1; Tempj < cur_node_id_sum; Tempj++)//遍历Tempi后面的模拟节点
                    {
                        cur_node_id[Tempi] = cur_node_id[Tempj];//把后一位模拟节点向前移
                    }
                    cur_node_id[cur_node_id_sum - 1] = 0x00;//最后一位置0
                    cur_node_id_sum--;
                    if(cur_node_id[Tempi] < ecu_node_id)//前节点,发送时要往左移。后节点不用管。
                      cur_node_id_n--;
                    if(cur_node_id_sum == 1)//最后只剩下ECU的节点时,清空cur_node_id和当前节点总个数
                    {
                        cur_node_id_sum = 0;
                        cur_node_id_n = 0;
                    }
                    break;
                }
            }
        }
        for (Tempi = 0; Tempi < cur_node_id_sum; Tempi++)  //冒泡排序,ID从小到大排序
        {
            for (Tempj = Tempi+1; Tempj < cur_node_id_sum; Tempj++)
            {
                if (cur_node_id[Tempi] > cur_node_id[Tempj])
                {
                    value = cur_node_id[Tempi];
                    cur_node_id[Tempi] = cur_node_id[Tempj];
                    cur_node_id[Tempj] = value;
                }
            }
        }
    }
    else//所有节点都需要操作
    {
        for (Tempk = 1; Tempk <= elCount(node_id); Tempk++)
          alignNodes(Tempk,node_behavior);
    }
}
void nodeBehavior(byte sim_node_n, byte node_behavior)//模拟节点行为
{
    //sim_node_n:node_id数组里的节点;以1为起始,1表示第一个节点;0xFF表示所有节点
    //node_behavior:代表模拟节点的行为
    int Tempi,Tempk;
    if(sim_node_n != 0xFF)//单个节点
    {
        if(node_behavior == gSim_on)//加入
        {
            sendAlive(sim_node_n);//单个节点加入,先声明,发送Alive
            sendRing(sim_node_n,gNMStateCode_Ring);//再发送ring
        }
        else if(node_behavior == gSim_out)//丢失
        {
            if(cur_node_id_sum <= 1)//丢失完以后,停止发送
              cancelTimer(TringOperation);
        }
        else if(node_behavior == gSim_sleep)
        {
            gNMStateCode = gNMStateCode_sleepInd;//建环时,满足睡眠条件时ind置1
        }
        else if(node_behavior == gSim_awake)
        {
            gNMStateCode = gNMStateCode_Ring;//建环时,满足睡眠条件->不满足睡眠条件
        }
    }
    else//所有节点
    {
      for (Tempk = 1; Tempk <= elCount(node_id); Tempk++)
        nodeBehavior(Tempk,node_behavior);
    }  
}
void nodeSwitch(byte sim_node_n, byte node_behavior)//模拟节点开关
{
    int Tempi;
    alignNodes(sim_node_n,node_behavior);//重组模拟节点
  
    if(cur_node_id_sum >= 2)//包含ECU 在内的节点
    {
        write("当前所有节点[%d] %x",cur_node_id_sum,cur_node_id[0]);
        for (Tempi = 1; Tempi < cur_node_id_sum; Tempi++)
          writeEx(1,0," %x",cur_node_id[Tempi]);
    }
    else
      write("当前所有节点[0] ");
    nodeBehavior(sim_node_n,node_behavior);
}
  • 效果
  • CAPL声明:
    在这里插入图片描述
  • CAPL打印:
    在这里插入图片描述
节点加入并建环
  • 先声明模拟节点存在,发送Alive报文
  • 按排序后从小到大依次发送
  • 发送最大节点ID时,后继节点变为最小节点ID

节点建环图示:
在这里插入图片描述

void sendAlive(byte sim_node_n)//alive报文
{
    //sim_node_n:node_id数组里的节点;以1为起始,1表示第一个节点;0xFF表示所有节点
    g_Tx.id = node_id[sim_node_n-1];
    g_Tx.byte(0) = node_id[sim_node_n-1] - node_id_min;
    g_Tx.byte(1) = gNMStateCode_Alive;
    output(g_Tx);
}
void sendRing(byte sim_node_n, byte msg_type)//ring报文
{
    gNMStateCode = msg_type;
    if(msg_type == gNMStateCode_sleepAck)//模拟节点发送置1的ack位/32
    {
        cancelTimer(TringOperation);
        g_Tx.id = node_id[sim_node_n-1];
        g_Tx.byte(0) = desNodeAddress(node_id[sim_node_n-1]);
        g_Tx.byte(1) = gNMStateCode;
        output(g_Tx);
    }
    else//02/12/
    {
        if(cur_node_id_sum <= 2)//已建环时不能重启定时器,周期会出错
          setTimer(TringOperation,100);
    }  
}
on timer TringOperation//ring报文 模拟节点周期发送
{
    if(cur_node_id_n >= cur_node_id_sum)//后继节点移位
      cur_node_id_n = 0;
    g_Tx.id = cur_node_id[cur_node_id_n];//id
    g_Tx.byte(0) = desNodeAddress(cur_node_id[cur_node_id_n]);//后继节点
    g_Tx.byte(1) = gNMStateCode;//ring报文类型
    outputNode();
    cur_node_id_n++;
    setTimer(TringOperation,100);
}
  • 效果
    在这里插入图片描述
节点丢失
  • 效果
    在这里插入图片描述
节点跳过
  • 根据OsekVDX NM 2.5.2规范,节点需要具有监测自身是否被跳过的能力,如果检测到被跳过,则重新发送alive报文

如下图:
在这里插入图片描述

  • 效果
    在这里插入图片描述
测试例子
  • 关注公众号【总线网络】代码自取
void MainTest ()
{
    NewTestCase0();//ECU节点发送机制
    NewTestCase1();//所有节点建环并丢失
    NewTestCase2();//单个节点建环并丢失
    NewTestCase3();//三个节点建环并丢失后继节点
    NewTestCase4();//三个节点建环跳过ECU节点
    testWaitForMeasurementEnd(10);
    stop();
}
总结

选择合适自己的写脚本的方式,能事半功倍!祝福各位学习CANoe的同学能早日毕业!
有不完善的地方欢迎留言或加我vx补充。
请输入公众号:总线网络。关注我,获取汽车网络开发及测试方面资料,更新干货!
分享总线开发知识
分享CAN/CANFDLIN/ETH等网络资料
分享CANoe/TSMaster/PCAN等设备工具使用
分享UDS/NM/Bootloader测试用例等
一起来学习,进步,交流吧!
在这里插入图片描述

  • 6
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值