You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS Studio中CANoe.NET API事件过程实现的正确性及替代方案咨询

关于CANoe.NET API事件驱动测试用例的合规实现方式

核心需求:发送ID为0x1BFDEF00、DLC=12的CAN报文3次,通过OnCANFrame事件校验报文DLC是否为12。最初事件方法未生效,直到创建并激活Observer后才正常运行,现咨询除已有方案外的其他合规实现方式。

已实现的可行代码

public override void Main()
{
    ICheck check = new MyUserCheck("MyUser check", "Check DLC of message XCP");
    check.Activate();
    LL2015.LL2015(); // 测试方法调用
    check.Deactivate();
}

public class Test_LL2015
{
    [TestCase("LL2015")]
    public void LL2015()
    {
        byte[] data = new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        CANFrame msg = new CANFrame(0x1BFDEF00, 12); // 定义CAN帧
        msg.SetRawData(data);
        msg.Send(); // 发送帧
        Execution.Wait(3000);
        msg.Send(); // 再次发送帧
        Execution.Wait(3000);
        msg.Send(); // 第三次发送帧
        Execution.Wait(3000);
    }
}

public class MyUserCheck : UserCheck
{
    public MyUserCheck(string title, string description)
        : base(title, description)
    {
    }

    [OnCANFrame(2, 0x1BFDEF00)]
    public void FrameReceived1(CANFrame frame)
    {
        if (frame.DLC != 12)
            Report.Violation("DLC check for frame 0x1BFDEF00 failed");
        else
        {
            Report.TestStepPass("[passed]");
            Report.TestStep(frame.TimeNS.ToString());
        }
    }
}

已尝试的两种方案

方案1:事件方法声明在主类中

public class MainClass : TestModule
{
    // ... 其他代码

    public override void Main()
    {       
        LL2015.LL2015(); // 测试方法调用
    }

    [OnCANFrame(2, 0x1BFDEF00)]
    public void FrameReceived1(CANFrame frame)
    {
        if (frame.DLC != 12)
        {
            Report.TestStepFail("[failed]");
        }     
        else
        {
            Report.TestStepPass("[passed]");
            Report.TestStep(frame.TimeNS.ToString());
        }
    }
}

方案2:基于ICANMessageObserver的独立监听器实现(MSpiller方案翻译)

这种方式通过实现ICANMessageObserver接口解耦监听逻辑,便于复用和精确控制生命周期:

public class DLCObserver : ICANMessageObserver
{
    private readonly uint _targetId;
    private readonly int _expectedDlc;
    private int _receivedCount = 0;
    private readonly int _expectedCount;

    public DLCObserver(uint targetId, int expectedDlc, int expectedCount)
    {
        _targetId = targetId;
        _expectedDlc = expectedDlc;
        _expectedCount = expectedCount;
    }

    // 对外暴露接收计数,便于测试断言
    public int ReceivedCount => _receivedCount;

    public void Update(ICANMessage message)
    {
        if (message.Id != _targetId) return;

        _receivedCount++;
        if (message.DLC != _expectedDlc)
        {
            Report.TestStepFail($"第{_receivedCount}次接收报文0x{_targetId:X8},DLC={message.DLC},不符合预期值{_expectedDlc}");
        }
        else
        {
            Report.TestStepPass($"第{_receivedCount}次接收报文0x{_targetId:X8},DLC校验通过");
            Report.TestStep($"接收时间:{message.TimeNS.ToString()}");
        }

        // 达到预期接收次数后自动停止监听
        if (_receivedCount >= _expectedCount)
        {
            CAN.MessageObservers.Remove(this);
        }
    }
}

// 测试模块中使用监听器
public class MainClass : TestModule
{
    public override void Main()
    {
        // 注册监听器
        var dlcObserver = new DLCObserver(0x1BFDEF00, 12, 3);
        CAN.MessageObservers.Add(dlcObserver);

        // 执行报文发送逻辑
        LL2015.LL2015();

        // 等待所有报文接收完成
        Execution.WaitUntil(() => dlcObserver.ReceivedCount >= 3, 10000);
    }
}

public class Test_LL2015
{
    [TestCase("LL2015")]
    public void LL2015()
    {
        byte[] data = new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        CANFrame msg = new CANFrame(0x1BFDEF00, 12);
        msg.SetRawData(data);
        
        // 简化循环发送逻辑
        for (int i = 0; i < 3; i++)
        {
            msg.Send();
            Execution.Wait(3000);
        }
    }
}

其他合规实现说明

1. TestFixture结构化测试

针对复杂测试场景,可将测试逻辑与监听逻辑封装在TestFixture中,利用Setup/TearDown管理监听生命周期:

[TestFixture]
public class CANFrameDlcTestFixture
{
    private DLCObserver _dlcObserver;

    [SetUp]
    public void Setup()
    {
        // 初始化监听器并注册
        _dlcObserver = new DLCObserver(0x1BFDEF00, 12, 3);
        CAN.MessageObservers.Add(_dlcObserver);
    }

    [TestCase("LL2015")]
    public void TestFrameDlc()
    {
        byte[] data = new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        CANFrame msg = new CANFrame(0x1BFDEF00, 12);
        msg.SetRawData(data);
        
        for (int i = 0; i < 3; i++)
        {
            msg.Send();
            Execution.Wait(3000);
        }

        // 等待接收完成并断言
        Execution.WaitUntil(() => _dlcObserver.ReceivedCount >= 3, 10000);
        Assert.AreEqual(3, _dlcObserver.ReceivedCount, "未收到预期数量的报文");
    }

    [TearDown]
    public void TearDown()
    {
        // 清理监听器
        if (_dlcObserver != null)
        {
            CAN.MessageObservers.Remove(_dlcObserver);
        }
    }
}

2. 特性驱动的全局监听优化

如果使用主类绑定事件的方案,可通过TestModule的生命周期特性,确保事件仅在测试执行期间生效:

public class MainClass : TestModule
{
    private bool _isListening = false;

    [OnStart]
    public void OnTestStart()
    {
        _isListening = true;
    }

    [OnStop]
    public void OnTestStop()
    {
        _isListening = false;
    }

    public override void Main()
    {       
        LL2015.LL2015();
    }

    [OnCANFrame(2, 0x1BFDEF00)]
    public void FrameReceived1(CANFrame frame)
    {
        // 仅在测试执行期间处理事件
        if (!_isListening) return;
        
        if (frame.DLC != 12)
        {
            Report.TestStepFail("[failed]");
        }     
        else
        {
            Report.TestStepPass("[passed]");
            Report.TestStep(frame.TimeNS.ToString());
        }
    }
}

内容的提问来源于stack exchange,提问作者Diana Bîrla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:10:41