CANoe CAPL 기반 UDS 진단 테스트 스크립트: 세션 제어, DID 읽기 및 DTC 상태 검증

UDS 서비스 자동화 테스트를 위한 CAPL 구현 패턴

CANoe에서 CAPL을 사용하여 UDS(Unified Diagnostic Services) 테스트를 자동화할 때 기본적인 통신 프레임워크와 테스트 로직을 체계적으로 구성하는 것이 중요하다. 아래는 10번(세션 제어), 22번(DID 읽기), 19번(DTC 읽기) 서비스를 중심으로 한 실용적인 코드 예제와 핵심 포인트이다.

요청 전송 및 응답 처리 함수 설계

재사용성을 높이기 위해 요청 메시지를 조합하고, 응답을 비동기로 처리하는 구조를 추천한다.

variables
{
    message 0x711 RequestMsg;     // 요청 CAN ID
    message 0x732 ResponseMsg;    // 응답 수신 ID
    byte rxBuffer[40];
    int rxLength;
    byte expectedResponseSID;
}

// 표준 단프레임 요청 생성 (예: 10, 22 서비스)
void UdsSendRequest(byte reqSID, byte subFunc, byte param[])
{
    byte dataLen = 2;
    if (reqSID == 0x22) dataLen = 4;

    RequestMsg.dlc = dataLen;
    RequestMsg.byte(0) = 0x03;           // 길이 정보 (N_PCI 포함)
    RequestMsg.byte(1) = reqSID;
    RequestMsg.byte(2) = subFunc;

    if (reqSID == 0x22)
    {
        RequestMsg.byte(3) = param[0];  // DID High Byte
        RequestMsg.byte(4) = param[1];  // DID Low Byte
    }

    output(RequestMsg);
    expectedResponseSID = reqSID + 0x40; // 예: 0x10 → 0x50
}

// 응답 메시지 이벤트 핸들러
on message ResponseMsg
{
    if (this.byte(0) != 0x03) return; // 유효한 프레임 아님

    byte rspSID = this.byte(1);
    byte nrc = this.byte(2);

    if (rspSID == expectedResponseSID)
    {
        // 정상 응답 처리
        if (rspSID == 0x50)
        {
            write("✅ 세션 변경 성공: %d", this.byte(3));
        }
        else if (rspSID == 0x62)
        {
            word didValue = (this.byte(3) << 8) | this.byte(4);
            handleDidReadSuccess(this.byte(2), didValue); // subFunc, 값 전달
        }
    }
    else if (rspSID == 0x7F && this.byte(2) == expectedResponseSID - 0x40)
    {
        handleNegativeResponse(nrc);
    }
}

10 서비스: 세션 전환 테스트 케이스

디폴트 세션과 프로그래밍 세션 간 전환을 검증하며, 보안 접근 필요 시 재시도 로직 포함.

testcase TC_DefaultSession()
{
    UdsSendRequest(0x10, 0x01, {0, 0}); // Default Session
    wait(200);
    // 응답은 위의 on message 블록에서 처리됨
}

testcase TC_ProgrammingSession()
{
    UdsSendRequest(0x10, 0x02, {0, 0});
    wait(300);

    // 가정: NRC 0x24(SecurityAccessRequired) 발생 시
    if (getLastNrc() == 0x24)
    {
        performSecurityUnlock();
        UdsSendRequest(0x10, 0x02, {0, 0}); // 재시도
    }
}

22 서비스: DID 데이터 읽기 및 파싱

DID 0xF40A(가상 차속) 값을 읽고, 의미 있는 범위 내에 있는지 확인.

testcase TC_ReadVehicleSpeed()
{
    byte targetDID[] = {0xF4, 0x0A};
    UdsSendRequest(0x22, 0x00, targetDID);
    wait(150);
}

void handleDidReadSuccess(byte subFunc, word value)
{
    if ((subFunc == 0x00) && ((value > 300) || (value < 0)))
    {
        testWarning("⚠️  이상치 감지: 차속 값 %d km/h", value);
    }
}

19 서비스: DTC 존재성 및 상태 모니터링

DTC 리스트는 3바이트 단위로 인코딩되며, 상태 바이트와 함께 전송된다. 특정 DTC(P1234)의 존재 여부를 확인하고, ECU 전원 사이클 후 복구 동작을 검증.

testcase TC_VerifyDtcPresent()
{
    UdsSendRequest(0x19, 0x0A, {0xFF}); // 모든 보고 가능한 DTC 요청
    wait(200);
    if (!containsDtc(0x1234))
    {
        testFail("❌ 필수 DTC(P1234) 없음");
    }
}

int containsDtc(long dtcCode)
{
    for (int i = 0; i < rxLength; i += 3)
    {
        long receivedDtc = (rxBuffer[i] << 16) | (rxBuffer[i+1] << 8) | rxBuffer[i+2];
        if ((receivedDtc & 0xFFFF) == dtcCode) return 1;
    }
    return 0;
}

// 전원 오프/온 사이클 테스트
testcase TC_DtcRecoveryAfterPowerCycle()
{
    // 1. 초기 DTC 확인
    TC_VerifyDtcPresent();

    // 2. ECU 전원 차단
    setSignal(Node_ECU1.PowerEnable, 0);
    delay(1000);

    // 3. 전원 차단 후 DTC 사라졌는지 확인
    UdsSendRequest(0x19, 0x0A, {0xFF});
    wait(200);
    if (containsDtc(0x1234)) testFail("❌ 전원 차단 후에도 DTC 유지됨");

    // 4. 전원 복구 및 재검증
    setSignal(Node_ECU1.PowerEnable, 1);
    delay(2000); // 부팅 대기
    cyclicUpdateTrigger(); // 진단 주기 실행 유도
    TC_VerifyDtcPresent(); // 복구 여부 확인
}

고급 팁: 상태 마스크 필터링 활용

특정 상태(DTC Status: pending, confirmed 등)만 필터링하여 읽을 수 있다. 예: 미확인(0x08) 상태의 DTC만 조회.

byte statusFilter = 0x08;
UdsSendRequest(0x19, 0x0A, &statusFilter);

이러한 기본 스크립트들은 신규 ECU 개발 초기 단계에서 통신 안정성과 기본 진단 동작을 신속하게 검증하는 데 매우 효과적이다. 특히 CAPL의 이벤트 기반 특성을 적절히 활용하면, 복잡한 상태 전이 테스트도 직관적으로 구현 가능하다.

태그: UDS CANoe CAPL Diagnostics DTC

9월 16일 18:20에 게시됨