IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> CANoe:第5个仿真工程:仿真+测试 -> 正文阅读

[开发测试]CANoe:第5个仿真工程:仿真+测试

目录

工程背景

工程目的

报文发送情况

工程实现

工程步骤概述

1 测试方法分析

1-1 检测报文周期

1-2 检测报文长度DLC

1-3?功能测试

1-4?检测未定义报文

2 添加Test Module

2-1创建测试环境

?2-2 插入CAPL Test Module

3?CAPL编写测试用例

?3-1?测试模块入口函数MainTest

3-2 CAPL测试用例——检测报文周期

3-3 CAPL测试用例——检测报文长度DLC

3-4 CAPL测试用例——检测未定义报文undefined msg

3-5 CAPL测试用例——功能测试

4 工程运行测试

?5 故障注入

5-1 系统变量控制报文发送

5-2? IG节点发送自定义报文

?6 测试报告


工程背景

本工程主要目的是: 基于第3个仿真工程, 熟悉CANoe的报文测试功能。

工程目的

本工程将围绕CAN总线中的报文,在Test Module中实现测试功能。主要包括:

  1. 检测周期性报文的周期
  2. 检测报文的长度
  3. 检查网络中是否有未定义的报文
  4. 简单的功能测试:通过修改相关系统变量的数值,模拟真实测试环境的操作,最后检验总线上的信号数值的改变。
  5. 生成测试报告

报文发送情况

第3个仿真工程报文发送与接收情况如下:

报文的相关属性整理如下表:

报文中的信号属性如下表:

?

工程实现

工程步骤概述

本章实例基于第12章的仿真工程,为了便于区别,需要将原工程的文件夹复制并 名为Vehicle_System_Simulation_Test, 工程名称也另存为Vehicle_System_CAN_Test.cfg, 在该工程文件夹下创建一个名为Testmodul的文件夹,用于存放相关的测试代码。

接下来,将在此基础上添加测试模块及故障注入面板等。

工程包括以下几个关键步骤:

  1. 测试方法分析
  2. 添加Test Module,
  3. CAPL测试用例编写,
  4. 运行工程,运行测试用例。
  5. 故障注入,运行测试用例。
  6. 查看测试报告。

1 测试方法分析

本工程的目的是分别测试不同报文的发送周期、报文长度DLC;功能测试(检测信号值是否在期望的数值范围内 );未定义报文。

1-1 检测报文周期

检测报文周期的方法是划定某一段特定的时间,选取该时间段中第一条待测报文作为起始时间戳,观察此待测报文后续重复的时间间隔。

TSL函数——检测函数

检测函数1

ChkStart_MsgAbsCycleTimeViolation ( Message aObservedMessage,duration aMinCycleTime, duration aMaxCycleTime)

函数功能:观察总线周期性报文aObservedMessage的每次出现,如果该报文的间隔不符合规范要求,则会触发一个代表异常出现的特殊事件。

返回值:>0,返回一个IDaCheckedId,即观察待测报文的事件;=0报错。

TSL函数——状态报告函数

状态报告函数1long ChKQuery_NumEvents(dword aCheckId)?

函数功能:查询该时间段内异常出现的特殊事件的个数

状态报告函数2double ChkQuery_StatProbeIntervalAvg(dword aCheckId)

返回该时间段内,该报文的平均周期间隔时间

状态报告函数3double ChkQuery_StatProbeIntervalMin(dword aCheckId)

返回该时间段内,该报文的最小周期间隔时间

状态报告函数4 double ChkQuery_StatProbeIntervalMax(dword aCheckId)

返回该时间段内,该报文的最大周期间隔时间

TSL函数——检测控制函数

检测控制函数1 long ChkControl_Destroy(Check aCheckId)

用于测试结束时,销毁该事件对象aCheckId,释放资源。返回0操作成功,<0报错。

1-2 检测报文长度DLC

函数:

  • 状态报告函数 ChkQuery_NumEvents

  • 检测控制函数 ChkControl_Destroy

  • 检测函数:dword ChkStart_InconsistentDLC(Message aMessage,char [] aCallback)

    • 检测发送到总线上指定报文长度是否与DBC数据库中的定义一致

    • aMessage待测报文;char [] aCallback回调函数名,可选参数

    • 返回值:>0:返回一个事件对象aCheckId;=0报错

1-3?功能测试

功能测试用过CAPL程序逻辑来设置某信号的数值,然后使用ChkStart_MsgSignalValueInvalid函数来检测信号值是否在期望的数值范围内。

dword ChkStart_MsgSignalValueInvalid (Signal aObservedSignal,double aMinValue, double aMaxValue, Callback aCallback)

函数参数:待测信号,必须是定在DBC中的信号,最小信号值,最大信号值,回调CAPL函数名,可选。

返回值:返回一个事件对象aCheckId,即检测未定义报文的事件

1-4?检测未定义报文

检测函数:dword ChkStart_UndefinedMessageReceived (char [] CaplCallback)

作用:观察当前总线上是否有未定义的报文

返回值:>0:返回一个事件对象aCheckId,即待观测报文的事件;=0报错。

状态报告函数:long ChkQuery_EventMessageId (dword aCheckId)

作用:返回触发该事件的报文的MessageId

返回值:>0返回触发该事件的报文ID;<0报错。

2 添加Test Module

2-1创建测试环境

创建测试环境,命名为NetworkTester。

?2-2 插入CAPL Test Module

插入CAPL Test Module,并配置此模块Configuration对话框

?配置Module的Name为:Network Tester,在TestModule文件夹下创建CAPL文件NetworkTester.can

3?CAPL编写测试用例

?选中TestModule,右键选中Edit,可以编辑NetworkTester.can 。

?3-1?测试模块入口函数MainTest

CAPL测试模块中can文件要求:-必须包含MainTest函数,所有的测试用例均从此接口进入

  1. 添加TestModule描述:Title,DisCription。
  2. 仿真工程初始化,确保报文的正常发送;本工程中为车门开锁,设置CarDriver,并且钥匙位置设置为2.
  3. 将测试用例分组testGroupBegin,测试用例函数名调用,testGroupEnd。
void MainTest()
{
  testModuleTitle("NetworkTester");
  testModuleDescription("Message Specification Test and Function Test Demo.");
  testGroupBegin("Check msg cycle time","Check the differ mesage cycle time");
    Init_Test_Condition();
    CheckMsgEngineData();
    CheckMsgVehicleData();
    CheckMsgGear_Info();
    CheckMsgIgnition_Info();
    CheckMsgLight_Info();
    testGroupEnd();
  
  testGroupBegin("Check msg DLC","Check DLC of a message");
  CheckDLCLock_Info();
  testGroupEnd();
  
  testGroupBegin("Check undefined msg","Check the undefined message");
  CheckUndefinedMessage();
  testGroupEnd();
  
  testGroupBegin("Fucntion Test","Check the engine speed after setup");
  CheckEngine_Speed();
  testGroupEnd();
  
}

//初始化仿真工程状态,确保各个模块处于Online
Init_Test_Condition()
{
  @Vehicle_Key::Unlock_Car = 1;
  @Vehicle_Key::Car_Driver = 0;
  @Vehicle_Key::Key_State = 2;
  testWaitForTimeout(500);
}

3-2 CAPL测试用例——检测报文周期

分别测试EngineData(50),VehicleData(50),Gear_Info(50),Ignition_Info(50) ,Light_Info(500)的报文周期。

以EngineData为例,CAPL程序逻辑如下:

  1. 先声明检测事件gCycCheckId
  2. ?定义常量:
    1. 周期最大最小范围值? :[lCycMinCycleTime,lCycMaxCycleTime] 为 [40,60]
    2. Light_Info的周期范围区间:[Light_MIN_CYCLE_TIME,Light_MAX_CYCLE_TIME] 为 [490,510]。
  3. 写测试用例: ?
    1. 定义测试报告提示信息:用testCaseTitle定义测试报告提示信息,以EngineData为例testCaseTitle("TC-1","TC-1:Check cycle time of msg EngineData");
    2. ?观测待测报文,周期是否在范围要求内:使用ChkStart_MsgAbsCycleTimeViolation观察待测报文是否在设置的区间范围内。正常返回报文检测事件ID——aCheckedId 。如果aCheckedId大于0代表测试正常运行,如果=0代表测过程有错误。
  4. 检测测试用例结果,输出测试报告后,销毁测试事件
    1. ?设定用例测试的时间 KTIMEOUT,在此观测时间段内观测待测报文的周期。
    2. 将测试用例运行结果aCheckedId 作为test条件——testAddCondition,这意味着将在报告中展示测试用例运行结果
    3. 等待测试时间结束,
    4. 观测结束后,统计测试用例中报文的周期平均值、最大值、最小值。
    5. 根据用例运行结果aCheckedId打印报告,如果aCheckedId的数量大于0,即检测报文周期存在不在范围内的情况,则用snprintf+TestStepFail 描述导致错误的测试步骤,测试用例的判决在此自动设置为失败。否则用snprintf+TestStepPass描述测试结果,报告中将显示按预期执行的测试步骤,顺利通过。
  5. 最后销毁检测事件gCycCheckId。

其他报文只需在3测试用例部分,改为相应的报文名称即可。1声明检测事件,4用例检测结果,5销毁检测事件都是通用功能。

具体代码示例如下:

variables
{
  //TC1
  dword gCycCheckId;//声明检测事件的ID
  int gUndefinedMsgCheckResult;//声明未定义报文的检测结果
  const long kMIN_CYCLE_TIME = 40;//一般最小周期时间常量
  const long kMAX_CYCLE_TIME = 60;//一般最大周期时间常量
  const long Light_MIN_CYCLE_TIME = 490;//定义报文Light_Info最小周期时间常量
  const long Light_MAX_CYCLE_TIME = 510;//定义报文Light_Info最大周期时间常量
  const long kTIMEOUT = 4000;//定义测试等待时间常量
  
  //自定义报文——使用IG模块
  
}

//周期时间检测结果函数
CheckMsgCyc(float aCycMinCycleTime, float aCycMaxCycleTime)
{
  long lQueryResultProbeAvg;//声明平均时间
  long lQueryResultProbeMin;//声明最小测量时间
  long lQueryResultProbeMax;//声明最大测量时间
  char lbuffer[100];
  
  testAddCondition(gCycCheckId);//在该函数中添加事件
  testWaitForTimeout(kTIMEOUT);//等待测试时间结束
  //统计平均时间
  lQueryResultProbeAvg = ChkQuery_StatProbeIntervalAvg(gCycCheckId);
  //统计min时间
  lQueryResultProbeMin = ChkQuery_StatProbeIntervalMin(gCycCheckId);
  //统计max时间
  lQueryResultProbeMax = ChkQuery_StatProbeIntervalMax(gCycCheckId);  
  
  if(ChkQuery_NumEvents(gCycCheckId)>0)
  {
    //统计异常次数//打印报告
    snprintf(lbuffer,elCount(lbuffer),"Valid values %.0fms - %.0fms",aCycMinCycleTime,aCycMaxCycleTime);
    testStepFail("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeAvg);
    testStepFail("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Min cycle time: %dms",lQueryResultProbeMin);
    testStepFail("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeMax);
    testStepFail("",lbuffer);
  }
  else
  {
    snprintf(lbuffer,elCount(lbuffer),"Valid values %.0fms - %.0fms",aCycMinCycleTime,aCycMaxCycleTime);
    testStepPass("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeAvg);
    testStepPass("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Min cycle time: %dms",lQueryResultProbeMin);
    testStepPass("",lbuffer);
    snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeMax);
    testStepPass("",lbuffer);
  }
  ChkControl_Destroy(gCycCheckId);//销毁事件
  
}

//TC1:Check Cycle time of msg EngineData
testcase CheckMsgEngineData()
{
  float lCycMinCycleTime;//声明最小周期时间
  float lCycMaxCycleTime;//声明最大周期时间
  lCycMinCycleTime = kMIN_CYCLE_TIME;//赋值
  lCycMaxCycleTime = kMAX_CYCLE_TIME;
  //测试报告提示信息
  testCaseTitle("TC-1","TC-1:Check cycle time of msg EngineData");
  //开始观察待测报文
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(EngineData,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);//周期时间检测结果函数
  testRemoveCondition(gCycCheckId);//移除测试条件
}
//TC-2:Check Cycle time of msg VehicleData
testcase CheckMsgVehicleData()
{
  float lCycMinCycleTime;
  float lCycMaxCycleTime;
  lCycMinCycleTime = kMIN_CYCLE_TIME;
  lCycMaxCycleTime = kMAX_CYCLE_TIME;
  
  
  testCaseTitle("TC-2","TC-2:Check cycle time of msg VehicleData");
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(VehicleData,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
  testRemoveCondition(gCycCheckId);
}
//TC-3:Check Cycle time of msg  Gear_Info 
testcase CheckMsgGear_Info()
{
  float lCycMinCycleTime;
  float lCycMaxCycleTime;
  lCycMinCycleTime = kMIN_CYCLE_TIME;
  lCycMaxCycleTime = kMAX_CYCLE_TIME;
  
  
  testCaseTitle("TC-3","TC-3:Check cycle time of msg Gear_Info");
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Gear_Info,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
  testRemoveCondition(gCycCheckId);
}
//TC-4:Check Cycle time of msg  Ignition_Info 
testcase CheckMsgIgnition_Info()
{
  float lCycMinCycleTime;
  float lCycMaxCycleTime;
  lCycMinCycleTime = kMIN_CYCLE_TIME;
  lCycMaxCycleTime = kMAX_CYCLE_TIME;
  
  testCaseTitle("TC-4","TC-4:Check cycle time of msg Ignition_Info");
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Ignition_Info,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
  testRemoveCondition(gCycCheckId);
}
//TC-5:Check Cycle time of msg  Light_Inf
testcase CheckMsgLight_Info()
{
  float lCycMinCycleTime;
  float lCycMaxCycleTime;
  lCycMinCycleTime = kMIN_CYCLE_TIME;
  lCycMaxCycleTime = kMAX_CYCLE_TIME;
  
  testCaseTitle("TC-5","TC-5:Check cycle time of msg Light_Info");
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Light_Info,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
  testRemoveCondition(gCycCheckId);
}


3-3 CAPL测试用例——检测报文长度DLC

//TC6:DLC 报文长度测试
testcase CheckDLCLock_Info()
{
  dword checkId;
  //测试报告提示信息
  testCaseTitle("TC-6","TC-6:Check msg DLC of Lock_Info");
 //管事观测报文Lock_Info的DLC
  checkId = ChkStart_InconsistentDlc(Lock_Info);
  testAddCondition(checkId);
  //等待测试时间结束
  testWaitForTimeout(kTIMEOUT);
  testRemoveCondition(checkId);
}

3-4 CAPL测试用例——检测未定义报文undefined msg

//TC-7:检测未定义信号
testcase CheckUndefinedMessage()
{
  long lEventUndefineMessageId;//声明未定义报文Id
  char lbuffer[100];
  
  gUndefinedMsgCheckResult = 0;//?初始化未定义报文数量为0
  testCaseTitle("TC-7","TC-7:Check CAN channel for undefined message");
  //开始观测当前总线
  gCycCheckId = ChkStart_UndefinedMessageReceived("UndefinedMsgCallback");
  //延时,即测量该时间段
  testWaitForTimeout(kTIMEOUT);
  
  switch(gUndefinedMsgCheckResult)
  {
    case 1:
      write("undefined message detected!");
      //获取未定义报文ID
      lEventUndefineMessageId = ChkQuery_EventMessageId(gCycCheckId);
      snprintf(lbuffer,elCount(lbuffer),"Undefined message detected: Id 0x%x",lEventUndefineMessageId);
      testStepFail("",lbuffer);
      break;
    default:
      write("Iamdefault");
      testStepPass("","No undefined message detected!");
      break;   
  }
  ChkControl_Destroy(gCycCheckId);//销毁事件
}

UndefinedMsgCallback(dword aCheckId)
{
  //回调函数,检测到未定义报文时调用
  write("Test: undefined message finded");
  ChkQuery_EventStatusToWrite(aCheckId);
  gUndefinedMsgCheckResult=1;//将未定义报文个数置为1
}

3-5 CAPL测试用例——功能测试

testcase CheckEngine_Speed()
{
  dword checkId;
  testCaseTitle("TC-8","TC-8:Check Engine Speed Value");
  @Vehicle_Key::Unlock_Car = 1;
  @Vehicle_Key::Car_Driver = 0;
  @Vehicle_Key::Key_State = 2;
  @Vehicle_Control::Eng_Speed = 2000;
  
  //开始观测,信号值是否在范围内
  checkId = ChkStart_MsgSignalValueInvalid(EngineData::EngSpeed,1900,2100);
  testWaitForTimeout(kTIMEOUT);
  if(ChkQuery_EventSignalValue(checkId))
  {
    testStepPass("","Correct Engine Speed Value");
  }
  else
  {
    testStepFail("","Incorrect Engine Speed Value");
  }
}

4 工程运行测试

运行工程后,运行测试。

测试用例运行结果如图所示,Light_Info的报文周期检测报错。

——查看CANdb,发现只有报文Light_Info的报文周期为500,代码中使用的检测范围为40-60,将周期范围更改为Light_MIN_CYCLE_TIME和Light_MAX_CYCLE_TIME,如下示例:

//TC-5:Check Cycle time of msg  Light_Info
testcase CheckMsgLight_Info()
{
  float lCycMinCycleTime;
  float lCycMaxCycleTime;
  //lCycMinCycleTime = kMIN_CYCLE_TIME;//kMIN_CYCLE_TIME=40==>Light_MIN_CYCLE_TIME=490
  //lCycMaxCycleTime = kMAX_CYCLE_TIME;//kMAX_CYCLE_TIME=60==>Light_MAX_CYCLE_TIME=510
  lCycMinCycleTime = Light_MIN_CYCLE_TIME;
  lCycMaxCycleTime = Light_MAX_CYCLE_TIME;
  
  testCaseTitle("TC-5","TC-5:Check cycle time of msg Light_Info");
  gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Light_Info,lCycMinCycleTime,lCycMaxCycleTime);
  CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
  testRemoveCondition(gCycCheckId);
}

再次运行测试用例,全部通过。

?5 故障注入

为了验证测试用例的正确性,可以使用多种故障注入方法实现故障注入,常见的有:使用故障注入函数,采用网络节点CAPL编程,以及使用IG节点。

书中制作了一个面板: Msg_Switch和Custom_Msg,分别控制报文的发送和关闭,以及发送自定义报文。

?我自己尝试使用Panel模块实现这个面板:创建系统变量,将面板复选框关联系统变量,再在CAPL编程中读取系统变量,根据变量值控制对应的报文函数。

但最后,使用Panel只实现了Msg_Switch部分。Custom_Msg没有找到对应的未定义报文创建方法,有实现的小伙伴欢迎分享。最后使用IG节点实现了未定义报文的发送。

5-1 系统变量控制报文发送

创建系统变量,将面板复选框关联系统变量,再在CAPL编程中读取系统变量,根据变量值控制对应的报文函数。

测试用例涉及7个报文,因而创建7个系统变量创建如下:

?以Gateway_EngineData_off为例,系统变量创建过程如下。

先建立一个Value Table共同使用。因为7个系统变量具有相同的数值解释:勾选为1代表停止报文发送,不勾选为0代表报文正常发送。使用相同的ValueTable可以统一管理。

?Panel创建过程如下:

先添加一个Panel面板,命名为NetworkTest.

添加复选框组件,修改名称并,关联对应的系统变量,下图示例为EngineData,其余报文控制根据名称一一对应即可。

?Panel创建完毕后,在NetworkTest.can中添加变量监听事件,控制相应的报文发送。

以EngineData为例,CAPL编程如下:

on sysvar_update TestSysVar::Gateway_EngineData_off
{
  if (@this==1)
  {
    testDisableMsg(EngineData);
    write("Test: disable EngineData");
    //testDisableMsg(Cluster_Info);
   // ILDisableMsg("Cluster_Info");
  }
  else{
    testEnableMsg(EngineData);
    write("Test: enable EngineData");
  }
}

控制报文发送和终止的函数不止有testDisableMsg和testEnableMsg,其它函数可以参考此文(完善后放链接)。

5-2? IG节点发送自定义报文

IG节点可分为?CAN IG和IG,区别是CANIG只支持CAN报文,而IG可支持CAN、LIN、MOST等其他报文。此外还有IG和PDU IG的区别,PDU IG可以支持任意网络协议,包括CAN以及Ethernet 、FlexRay。

?5-2-1 创建IG节点

在Simulation Setup总线上创建CAN IG 模块,CAN IG模块允许用户发送自定义的CAN报文。

5-2-2 添加自定义报文,并配置

?如上图添加了3个自定义报文Msg_01,Msg_02,Msg_03,并按下图定义相关属性

?5-2-3 运行

保存后,启动工程,启动测试用例,发送自定义报文。

测试结果显示检测到了未定义报文。

?6 测试报告

测试报告有2种格式:①CANoe TestReport Viewer(推荐)②XML/HTML格式(以前的)

可如下图所示,修改为自己所需的格式:

?测试报告的打开位置如下

①CANoe TestReport Viewer(推荐)

?②XML/HTML格式(以前的)

?

END?

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 12:01:59  更:2022-02-26 12:03:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 2:45:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码