当前位置: 首页 > news >正文

车载学习(8)——CAPL与诊断相关

CAPL与诊断相关

1.诊断脚本的编程思路

1.创建诊断请求变量
变量成CAPL编程器,右侧的symbol框进行查询。针对不同诊断类型进行选择
2.发送诊断请求
3.等待请求发送完毕、等待接收到响应。
4.获取诊断响应的响应码

2.诊断相关CPAL函数

2.1.创建诊断的请求变量

  • CAPL中使用diagRequest关键字声明"诊断请求变量"
  • 需使用CDD文件中定义的诊断命令限定名作为数据类型
  • 可以在CAPL Browser右侧Symbolics面板中查看并复制诊断命令的限定名。
  • 语法示例:
    • diagRequest ECU名.诊断命令限定名 变量名;
    • diagRequest 诊断命令限定名 变量名;
  diagRequest DefaultSession_Start reqDefaultSessin; // 进入默认会话的诊断请求diagRequest ExtendedDiagnosticSession_Start reqExtendedSessin; // 进入扩展会话的诊断请求diagRequest ProgrammingSession_Start reqProgSession; // 进入编程会话的诊断请求

2.2基本流程函数

  • diagSetTarget:设置后续诊断请求通讯的目标ECU
long diagSetTarget (char ecuName[])
  • diagSendRequest:发送指定的诊断请求
long diagSendRequest (diagRequest obj)
  • TestWaitForDiagRequestSent:等待直到指定诊断请求已发送
long TestWaitForDiagRequestSent (diagRequest request, dword timeout);
  • TestWaitForDiagResponse:等待直到接收到指定诊断请求的响应
long TestWaitForDiagResponse (diagRequest request, dword timeout);
  • diagGetResponseCode:返回指定诊断请求的最后收到响应码
    在这里插入图片描述
long diagGetLastResponseCode (diagRequest req);
long diagGetLastResponseCode ();
  • TestReportWriteDiagResponse:向测试报文中写入诊断响应的信息
TestReportWriteDiagResponse (diagRequest req);
  • 一个基础流程的TestCase:
 // 创建了一个进入扩展会话的“诊断请求变量”diagRequest DoorFL.ExtendedDiagnosticSession_Start reqExtendedSession;// 创建一个变量代表诊断响应返回的响应码long respCode;// 发送诊断请求diagSendRequest(reqExtendedSession);// 等待请求发送完毕if(testWaitForDiagRequestSent(reqExtendedSession, 1000) == 1){write("进入扩展会话的请求发送完毕");}else{write("进入扩展会话的请求发送失败");return;}// 等待收到诊断响应if(testWaitForDiagResponse(reqExtendedSession, 1000) == 1){write("进入扩展会话的响应已经收到");}else{write("进入扩展会话的响应未收到");return;}// 获取诊断响应的响应码respCode = diagGetLastResponseCode(reqExtendedSession);if(respCode == -1) // 肯定响应{write("进入扩展会话的响应为肯定响应");}else if(respCode == 0) // 未收到响应{write("进入扩展会话的响应还没收到");}else{write("进入会展会话的响应为否定响应,NRC为:0x%02X", respCode);}  // 将诊断响应信息写入测试报告testReportWriteDiagResponse(reqExtendedSession);

2.3请求报文数据的设置

  • diagSetParameter:设置请求中指定参数的物理值
long diagSetParameter (diagResponse obj, char parameterName[], double newValue) 
long diagSetParameter (diagRequest obj, char parameterName[], double newValue)
long diagSetParameter (diagResponse obj, long mode, char parameterName[], double newValue)
long DiagSetParameter (diagRequest obj, long mode, char parameterName[], double newValue)
  • diagSetParameterRaw:设置请求中指定参数的原始字节数组
long diagSetParameterRaw (diagResponse obj, char parameter[], byte* buffer, DWORD buffersize)
long diagSetParameterRaw (diagRequest obj, char parameterName[], byte* buffer, DWORD buffersize)
long diagGetParameterRaw (diagResponse obj, char parameterName[], byte* buffer, DWORD buffersize)
long diagGetParameterRaw (diagRequest obj, char parameterName[], byte* buffer, DWORD buffersize)
  • diagSetPrimitiveByte:设置请求报文中指定索引索引的单个字节
long diagSetPrimitiveByte( diagRequest request, DWORD bytePos, DWORD newValue);
long diagSetPrimitiveByte( diagResponse response, DWORD bytePos, DWORD newValue);
  • diagGetPrimitiveData:设置请求报文的原始字节数组
long diagGetPrimitiveData (diagResponse obj, byte* buffer, DWORD buffersize)
long diagGetPrimitiveData (diagRequest obj, byte* buffer, DWORD buffersize)
long diagSetPrimitiveData (diagResponse obj, byte* buffer, DWORD buffersize)
long diagSetPrimitiveData (diagRequest obj, byte* buffer, DWORD buffersize)
  • 向请求报文写指定参数的实例
diagRequest DID_WindowLiftRoughPosition_Write reqWindowLiftWrite;  // 声明一个诊断请求变量/*DiagSetPrimitiveByte —— 向诊断请求,设置一个原始字节*/
//  DiagSetPrimitiveByte(reqWindowLiftWrite, 0, 0x2E); // 将请求报文中第0个字节设置为0x2E
//  DiagSetPrimitiveByte(reqWindowLiftWrite, 1, 0x02); // 将请求报文中第1个字节设置为0x02
//  DiagSetPrimitiveByte(reqWindowLiftWrite, 2, 0x01); // 将请求报文中第2个字节设置为0x01
//  DiagSetPrimitiveByte(reqWindowLiftWrite, 3, 0x06); // 将请求报文中第3个字节设置为0x06/*DiagSetPrimitiveData —— 向诊断请求,设置一组原始字节*/
//  {
//    byte primitiveData[4] = {0x2E, 0x02, 0x01, 0x08};
//    DiagSetPrimitiveData(reqWindowLiftWrite, primitiveData, 4);
//  }/*DiagSetParameter —— 设置诊断请求中指定参数的值*/// diagSetParameter(reqWindowLiftWrite, "WindowLiftRoughPosition", 0x06);// diagSetParameter(reqWindowLiftWrite, "WindowLiftRoughPosition", "43,75%");/*DiagSetParameterRaw —— 设置诊断请求中指定的参数的原始字节数组*/{byte position[1] = {0x07};diagSetParameterRaw(reqWindowLiftWrite, "WindowLiftRoughPosition", position, 1);}// 发送诊断请求diagSendRequest(reqWindowLiftWrite);

2.4 响应报文数据的获取

  • diagGetRespParameter:获取响应中指定参数的物理值
double diagGetRespParameter (diagRequest req, char parameterName[]);
double diagGetRespParameter (diagRequest req, long mode, char parameterName[]);
  • diagGetRespParameterRaw:获取响应中指定参数的原始字节数组
long diagGetRespParameterRaw (diagRequest req, char parameterName[], byte buffer[], dword bufferLen);
  • diagGetRespPrimitiveSize:获取响应报文的字节个数
long diagGetRespPrimitiveSize( diagRequest request);
  • diagGetRespPrimitiveByte:获取响应报文的原始字节数组
long diagGetRespPrimitiveByte( diagRequest request, DWORD bytePos);
  • diagGetIterationCount:获取响应中指定的复合参数的值的个数
long diagGetIterationCount( diagRequest obj, char complexParamQualifier[]);
long diagGetIterationCount( diagResponse obj, char complexParamQualifier[]);
long diagGetRespIterationCount( diagRequest obj, char complexParamQualifier[]);
  • diagGetComplexRespParameter:获取响应中指定的复合参数中指定索引的参数值
long diagGetComplexRespParameter (diagRequest req, char parameterName[], dword iteration, char subParameter[], byte buffer[], dword bufferLen);
double diagGetComplexRespParameter (diagRequest req, char parameterName[], dword iteration, char subParameter[]); 
double diagGetComplexRespParameter (diagRequest req, long mode, char parameterName[], dword iteration, char subParameter[]);
  • 一个获取普通响应数据的测试Case
  diagRequest ExtendedDiagnosticSession_Start reqExtendedSession; // 创建一个诊断请求变量long respCode; // 创建一个响应码变量diagSendRequest(reqExtendedSession); // 发送诊断请求if(1 != testWaitForDiagResponse(reqExtendedSession, 1000)) { // 等待收到诊断响应write("进入扩展会话的诊断请求,未收到响应!");return;}respCode = diagGetLastResponseCode(reqExtendedSession);if(-1 != respCode){write("进入扩展会话的诊断请求,收到了否定响应,否定响应码为0x%02X", respCode);return;}write("进入扩展会话的诊断请求收到了肯定响应");/*diagGetRespParameter —— 获取诊断响应中指定参数的值(数值、字符串)*/
//  {
//    int sessionType; // 诊断响应中返回的当前会话类型
//    int p2;  // 诊断响应中返回的常规超时时间(P2 Server Time)
//    int p2ex;  //  诊断响应中返回的特殊超时时间(P2 extended Server Time)
//    
//    sessionType = diagGetRespParameter(reqExtendedSession, 1, "DiagSessionType");
//    p2 = diagGetRespParameter(reqExtendedSession, 1, "P2");
//    p2ex = diagGetRespParameter(reqExtendedSession, 1, "P2Ex");
//    write("当前的会话类型为:%d", sessionType);
//    write("P2时间为:%dms", p2);
//    write("P2Ex时间为:%dms", p2ex);
//  }/*diagGetRespParameterRaw —— 获取诊断响应中指定参数的原始字节数组*/
//  {
//    byte p2[2];
//    int i;
//    
//    diagGetRespParameterRaw(reqExtendedSession, "P2", p2, elCount(p2));
//    for(i=0; i < elCount(p2); i++)
//    {
//      write("诊断响应中参数P2的第%d个字节的值为:0x%02X", i, p2[i]);
//    }
//  }/*DiagGetRespPrimitiveSize —— 获取诊断响应中的原始报文的字节数量DiagGetRespPrimitiveByte —— 获取诊断响应中的原始报文中的指定索引上的字节*/{int primitiveSize;int i;primitiveSize = DiagGetRespPrimitiveSize(reqExtendedSession);write("诊断响应的报文原始字节数量为:%d", primitiveSize);for(i = 0; i < primitiveSize; i++){byte b;b = DiagGetRespPrimitiveByte(reqExtendedSession, i);write("诊断响应的原始报文中第%d个字节为0x%02X", i, b);}}
  • 获取响应复合数据的例子(诊断19 02 服务)
  diagRequest DoorFL.FaultMemory_ReadAllIdentified reqReadDTC;  // 创建一个诊断请求变量int respCode; // 创建一个诊断响应的响应码变量// 设置读取故障码请求的故障码状态掩码参数值为0x01diagSetParameter(reqReadDTC, "DTCStatusMask", 0x01);// 发送诊断请求diagSendRequest(reqReadDTC);// 等待直到收到诊断的响应if(1 != testWaitForDiagResponse(reqReadDTC, 1000)){write("读取故障码列表的诊断请求,未收到响应!");return;}// 获取诊断响应的响应码respCode = diagGetLastResponseCode(reqReadDTC);if(-1 != respCode){write("进入扩展会话的诊断请求,收到了否定响应,否定响应码为0x%02X", respCode);return;}{int dtcCount;int i;// 获取诊断响应中的指定的符合参数的参数值的个数dtcCount = diagGetRespIterationCount(reqReadDTC, "ListOfDTC");write("读取故障码请求的响应报文中,返回的故障信息的数量:%d", dtcCount);// 获取诊断响应中的每一组故障信息的参数值for(i = 0; i < dtcCount; i++){char dtcText[100];long dtc;int status;diagGetComplexRespParameter(reqReadDTC, "ListOfDTC", i, "DTC", dtcText, elCount(dtcText));dtc = diagGetComplexRespParameter(reqReadDTC, "ListOfDTC", i, "DTC");status = diagGetComplexRespParameter(reqReadDTC, "ListOfDTC", i, "StatusOfDTC");write("读取到的第%d个故障信息的故障码码值为:0x%06X,故障文本为:%s,状态码为:0x%02X", i, dtc, dtcText, status);}}

2.5 根据获取的种子计算key的函数

  • diagGenerateKeyFromSeed:根据种子值计算key依赖于dll文件
long diagGenerateKeyFromSeed ( byte seedArray[], dword seedArraySize, dword securityLevel, char variant[], char ipOption[], byte keyArray[], dword maxKeyArraySize, dword& keyActualSize); // form 1
long DiagGenerateKeyFromSeed(char ecuQualifier[], byte seedArray[] , dword seedArraySize, dword securityLevel, char variant[], char option[] , byte keyArray[], dword maxKeyArraySize, dword& keyActualSizeOut); // form 2
  • 一个获取ECU权限的Case:
  /*进入到扩展会话*/{diagRequest DoorFL.ExtendedDiagnosticSession_Start reqExtendSession;diagSendRequest(reqExtendSession);  // 发送“进入扩展会话”的诊断请求testWaitForDiagResponse(reqExtendSession, 1000);  // 等到收到诊断的响应if(diagGetLastResponseCode() != -1){write("进入扩展会话的诊断请求,未收到肯定响应");return;}}/*申请安全种子 --> 根据种子计算出Key -->发送Key来解锁ECU*/{diagRequest SeedLevel_0x01_Request reqGetSeed; // 获取种子的请求diagRequest KeyLevel_0x01_Send reqSendKey;  // 发送Key的请求byte seedArray[2];byte keyArray[255];dword keyActualSize;int i;diagSendRequest(reqGetSeed);  // 发送“获取安全种子”请求testWaitForDiagResponse(reqGetSeed, 1000);  // 等待收到诊断的响应if(diagGetLastResponseCode() != -1){write("获取安全种子的诊断请求,未收到肯定响应");return;}// 获取响应中返回的种子的字节数组diagGetRespParameterRaw(reqGetSeed, "SecuritySeed", seedArray, elCount(seedArray));write("获取到的安全种子的字节为:0x%02X 0x%02X", seedArray[0], seedArray[1]);// 根据种子计算出KeydiagGenerateKeyFromSeed(seedArray, elCount(seedArray), 1, "", "", keyArray, elCount(keyArray), keyActualSize);write("生成的Key的字节数:%d", keyActualSize);write("生成的Key的字节为:");for(i = 0; i < keyActualSize; i++){write("\t0x%02X", keyArray[i]);}// 给“发送Key”的请求设置参数(SecurityKey)diagSetParameterRaw(reqSendKey, "SecurityKey", keyArray, keyActualSize);// 发送key的请求diagSendRequest(reqSendKey);}

3.诊断小项目

3.1 0x10服务(切换会话功能)的测试

  • 切换到默认会话
  • 从默认会话切换到拓展会话
  • 从拓展会话切换到编程会话
    CAPL测试代码:
/*@!Encoding:936*/
includes
{}variables
{int respCode; // 诊断响应码diagRequest DefaultSession_Start reqDefaultSessin; // 进入默认会话的诊断请求diagRequest ExtendedDiagnosticSession_Start reqExtendedSessin; // 进入扩展会话的诊断请求diagRequest ProgrammingSession_Start reqProgSession; // 进入编程会话的诊断请求
}MainTest()
{diagSetTarget("DoorFL");testModuleTitle("DoorFL ECU 诊断功能的测试");testGroupBegin("ECU会话状态的切换", "切换到:默认会话、扩展会话、编程会话");TC1_DefaultSession();TC2_ExtendedSession();TC3_ProgrammingSession();testGroupEnd();
}testcase TC1_DefaultSession()
{testCaseTitle("TC1", "测试用例1:切换到默认会话");diagSendRequest(reqDefaultSessin); // 发送诊断请求testWaitForDiagResponse(reqDefaultSessin, 1000);  // 等待收到诊断的响应respCode = diagGetLastResponseCode(reqDefaultSessin);  // 获取诊断的响应码if(respCode == -1){int P2, P2Ex;testStepPass("进入默认会话", "进入默认会话成功,返回了肯定响应");P2 = diagGetRespParameter(reqDefaultSessin, 1, "P2");P2Ex = diagGetRespParameter(reqDefaultSessin, 1, "P2Ex");if(P2 == 150){testStepPass("常规超时时间参数P2", "P2时间符合预期值150ms");}else{testStepFail("常规超时时间参数P2", "P2时间不符合预期值150ms");}if(P2Ex == 2000){testStepPass("特殊超时时间参数P2Ex", "P2Ex时间符合预期值2000ms");}else{testStepFail("特殊超时时间参数P2Ex", "P2Ex时间不符合预期值2000ms");}}else if(respCode == 0){testStepFail("进入默认会话", "进入默认会话失败,未收到响应");}else{testStepFail("进入默认会话", "进入默认会话失败,返回了否定响应");}testReportWriteDiagResponse(reqDefaultSessin); // 将响应信息写入测试报告
}testcase TC2_ExtendedSession()
{testCaseTitle("TC2", "测试用例2:切换到扩展会话");diagSendRequest(reqExtendedSessin); // 发送诊断请求testWaitForDiagResponse(reqExtendedSessin, 1000);  // 等待收到诊断的响应respCode = diagGetLastResponseCode(reqExtendedSessin);  // 获取诊断的响应码if(respCode == -1){int P2, P2Ex;testStepPass("进入扩展会话", "进入扩展会话成功,返回了肯定响应");P2 = diagGetRespParameter(reqExtendedSessin, 1, "P2");P2Ex = diagGetRespParameter(reqExtendedSessin, 1, "P2Ex");if(P2 == 150){testStepPass("常规超时时间参数P2", "P2时间符合预期值150ms");}else{testStepFail("常规超时时间参数P2", "P2时间不符合预期值150ms");}if(P2Ex == 2000){testStepPass("特殊超时时间参数P2Ex", "P2Ex时间符合预期值2000ms");}else{testStepFail("特殊超时时间参数P2Ex", "P2Ex时间不符合预期值2000ms");}}else if(respCode == 0){testStepFail("进入扩展会话", "进入扩展会话失败,未收到响应");}else{testStepFail("进入扩展会话", "进入扩展会话失败,返回了否定响应");}testReportWriteDiagResponse(reqExtendedSessin); // 将响应信息写入测试报告
}testcase TC3_ProgrammingSession()
{testCaseTitle("TC3", "测试用例3:切换到编程会话");diagSendRequest(reqProgSession); // 发送诊断请求testWaitForDiagResponse(reqProgSession, 1000);  // 等待收到诊断的响应respCode = diagGetLastResponseCode(reqProgSession);  // 获取诊断的响应码if(respCode == -1){int P2, P2Ex;testStepPass("进入编程会话", "进入编程会话成功,返回了肯定响应");P2 = diagGetRespParameter(reqProgSession, 1, "P2");P2Ex = diagGetRespParameter(reqProgSession, 1, "P2Ex");if(P2 == 150){testStepPass("常规超时时间参数P2", "P2时间符合预期值150ms");}else{testStepFail("常规超时时间参数P2", "P2时间不符合预期值150ms");}if(P2Ex == 2000){testStepPass("特殊超时时间参数P2Ex", "P2Ex时间符合预期值2000ms");}else{testStepFail("特殊超时时间参数P2Ex", "P2Ex时间不符合预期值2000ms");}}else if(respCode == 0){testStepFail("进入编程会话", "进入编程会话失败,未收到响应");}else{testStepFail("进入编程会话", "进入编程会话失败,返回了否定响应");}testReportWriteDiagResponse(reqProgSession); // 将响应信息写入测试报告
}
  • 跟踪窗口
    在这里插入图片描述
  • testmodule 结果
    在这里插入图片描述

3.2 完成写入、读取ECU序列号的诊断功能的测试

  • 切换到拓展会话,解锁ECU
  • 写入ECU序列号
  • 读取ECU序列号并验证
  • 实际CAPL测试代码
/*@!Encoding:936*/
includes
{}variables
{int respCode; // 诊断响应码diagRequest ExtendedDiagnosticSession_Start reqExtendedSession; // 进入扩展会话的诊断请求diagRequest DoorFL.KeyLevel_0x01_Send reqKeyLevel_0x01; // 发送秘钥的诊断请求diagRequest DoorFL.SeedLevel_0x01_Request reqSeedLevel_0x01; //获取种子的诊断请求diagRequest DoorFL.SerialNumber_Write reqSerialNumber_W;  // 写序列号diagRequest DoorFL.SerialNumber_Read reqSerialNumber_R;  // 读序列号
}Maintest()
{diagSetTarget("DoorFL");  // 设置诊断目标ECUtestModuleTitle("ECU序列号的诊断功能测试");testModuleDescription("ECU序列号写入、读取,主要涉及诊断0x22、0x2E服务");TC1_Ecu_SerialNum_RW();}testcase TC1_Ecu_SerialNum_RW()
{byte seedArray[2];byte keyArray[8];dword keybyteSize;dword Serialnum;dword SerialnumRead;int i;testCaseTitle("TC1","测试用例1:ECU序列号读写诊断测试");// 进入到拓展会话diagSendRequest(reqExtendedSession);// 等待收到诊断的响应testWaitForDiagResponse(reqExtendedSession, 1000);  // 获取诊断响应的响应码if(diagGetLastResponseCode() != -1){write("进入拓展会话的诊断请求,未收到肯定响应");testStepFail("进入拓展会话失败");}else{write("进入拓展会话的诊断请求,收到肯定响应");testStepPass("成功切换为拓展会话");}// 申请安全种子diagSendRequest(reqSeedLevel_0x01); // 等待收到诊断的响应testWaitForDiagResponse(reqSeedLevel_0x01, 1000);  // 获取诊断响应的响应码if(diagGetLastResponseCode() != -1){write("申请安全种子的诊断请求,未收到肯定响应");testStepFail("申请安全种子失败");}else{write("申请安全种子的诊断请求,收到肯定响应");testStepPass("成功申请安全种子");}// 获取响应中返回的种子的字节数组diagGetRespParameterRaw(reqSeedLevel_0x01,"SecuritySeed",seedArray,elCount(seedArray));write("获取到的安全种子的字节为:0x%02X 0x%02X", seedArray[0], seedArray[1]);// 根据种子计算出keydiagGenerateKeyFromSeed(seedArray,elCount(seedArray),1,"","",keyArray,elCount(keyArray),keybyteSize);write("生成的Key的字节数:%d", keybyteSize);write("生成的Key的字节为:");for(i = 0;i < keybyteSize; i++){write("\t0x%02X", keyArray[i]);}// 给发送Key请求设置key参数diagSetParameterRaw(reqKeyLevel_0x01,"SecurityKey",keyArray,keybyteSize);// 发送Key的请求diagSendRequest(reqKeyLevel_0x01);// 等待收到诊断的响应testWaitForDiagResponse(reqKeyLevel_0x01, 1000); // 获取诊断响应的响应码if(diagGetLastResponseCode() != -1){write("发送key的诊断请求,未收到肯定响应");testStepFail("解锁ECU失败");}else{write("发送key的诊断请求,收到肯定响应");testStepPass("成功解锁ECU");}// 写入ECU序列号Serialnum = 0x11223344;diagSetParameter(reqSerialNumber_W, "SerialNumber", Serialnum);diagSendRequest(reqSerialNumber_W);// 等待收到诊断的响应testWaitForDiagResponse(reqSerialNumber_W, 1000); // 获取诊断响应的响应码if(diagGetLastResponseCode() != -1){write("写ECU序列号的诊断请求,未收到肯定响应");testStepFail("写ECU序列号请求失败");}else{write("写ECU序列号的诊断请求,收到肯定响应");testStepPass("写ECU序列号请求成功");}// 读取ECU序列号diagSendRequest(reqSerialNumber_R);// 等待收到诊断的响应testWaitForDiagResponse(reqSerialNumber_R, 1000); // 获取诊断响应的响应码if(diagGetLastResponseCode() != -1){write("读ECU序列号的诊断请求,未收到肯定响应");testStepFail("读ECU序列号请求失败");}else{write("读ECU序列号的诊断请求,收到肯定响应");testStepPass("读ECU序列号请求成功");}SerialnumRead =  diagGetRespParameter(reqSerialNumber_R,"SerialNumber");write("读取的ECU序列号0x%x",SerialnumRead);if(SerialnumRead != Serialnum){write("读写ECU序列号失败");testStepFail("读写ECU序列号失败");}else{write("读写ECU序列号成功");testStepPass("读写ECU序列号成功");}  }
  • trace:
    在这里插入图片描述
  • testreport
    在这里插入图片描述
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/254760.html

相关文章:

  • Ubuntu 安装Telnet服务
  • C#的泛型和匿名类型
  • 语音情感识别:CNN-LSTM 和注意力增强 CNN-LSTM 模型的比较分析
  • 在 Windows 上使用 Docker Desktop 快速搭建本地 Kubernetes 环境(附详细部署教程)
  • 死锁相关知识
  • MySQL 的 WITH ROLLUP 功能
  • WinUI3入门7:使用风格 共享控件样式
  • 以太网基础①以太网相关通信接口
  • Jmeter中常用的断言方法有哪些?
  • redis02
  • 机器学习竞赛中的“A榜”与“B榜”:机制解析与设计深意
  • Mac电脑 - Sublim Text 代码编辑器
  • el-image在表格中显示,弹出的预览图片被遮挡,如何解决
  • 基于Spring Boot+Vue的“暖寓”宿舍管理系统设计与实现(源码及文档)
  • Android13 增加产品配置文件
  • CMake实践:指定gcc版本编译和交叉编译
  • NetworkManager介绍与用法
  • linux路由
  • 2025-05-05-80x86汇编语言环境配置
  • 【职场算法】如何在合群与独立间找到最优解?
  • [接口-ihrm]
  • 图像特征检测算法SIFT
  • P2066 机器分配
  • 华为OD机试-MELON的难题-DFS(JAVA 2025A卷)
  • IntersectionObserver API应用场景示例代码详解
  • Netty PoolChunk依赖的自定义数据结构:IntPriorityQueue和LongLongHashMap
  • 计算机网络:(五)信道复用技术,数字传输系统,宽带接入技术
  • C++中所有数据类型
  • CppCon 2017 学习:folly::Function A Non-copyable Alternative to std::function
  • 目标检测之YOLOV11自定义数据使用OBB训练与验证