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
相关产品推荐
相关产品推荐

