286 lines
7.0 KiB
C++
286 lines
7.0 KiB
C++
// test_handler.cpp - Hardware Test Handler Implementation
|
|
|
|
#include "test_handler.h"
|
|
#include "can_handler.h"
|
|
#include "task_config.h"
|
|
#include "psram_buffer.h"
|
|
|
|
TestConfig testConfig;
|
|
TestResult testResult;
|
|
static uint32_t expectedSequence = 0;
|
|
static uint32_t receivedSequence = 0;
|
|
static TaskHandle_t testTaskHandle = NULL;
|
|
|
|
void initTestHandler() {
|
|
testConfig.mode = TEST_MODE_IDLE;
|
|
testConfig.frameCount = 1000;
|
|
testConfig.intervalUs = 1000;
|
|
testConfig.canId = 0x100;
|
|
testConfig.dataLen = 8;
|
|
testConfig.useFD = true;
|
|
testConfig.running = false;
|
|
|
|
memset(&testResult, 0, sizeof(TestResult));
|
|
}
|
|
|
|
uint32_t generateTestFrame(uint32_t sequence, CanFrame& frame) {
|
|
frame.timestamp = micros();
|
|
frame.id = testConfig.canId;
|
|
frame.len = testConfig.useFD ? (testConfig.dataLen > 8 ? testConfig.dataLen : 8) : 8;
|
|
frame.flags = testConfig.useFD ? 0x01 : 0x00;
|
|
|
|
frame.data[0] = (sequence >> 24) & 0xFF;
|
|
frame.data[1] = (sequence >> 16) & 0xFF;
|
|
frame.data[2] = (sequence >> 8) & 0xFF;
|
|
frame.data[3] = sequence & 0xFF;
|
|
frame.data[4] = 0xDE;
|
|
frame.data[5] = 0xAD;
|
|
frame.data[6] = 0xBE;
|
|
frame.data[7] = 0xEF;
|
|
|
|
for (int i = 8; i < frame.len && i < 64; i++) {
|
|
frame.data[i] = (uint8_t)(sequence + i);
|
|
}
|
|
|
|
return sequence;
|
|
}
|
|
|
|
bool validateTestFrame(const CanFrame& frame, uint32_t& expectedSeq) {
|
|
if (frame.id != testConfig.canId) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t receivedSeq = ((uint32_t)frame.data[0] << 24) |
|
|
((uint32_t)frame.data[1] << 16) |
|
|
((uint32_t)frame.data[2] << 8) |
|
|
((uint32_t)frame.data[3]);
|
|
|
|
if (receivedSeq != expectedSeq) {
|
|
uint32_t lost = receivedSeq - expectedSeq;
|
|
testResult.framesLost += lost;
|
|
expectedSeq = receivedSeq + 1;
|
|
return true;
|
|
}
|
|
|
|
expectedSeq++;
|
|
return true;
|
|
}
|
|
|
|
void testTxTask(void *pvParameters) {
|
|
Serial.println("Test TX Task started");
|
|
|
|
uint32_t sequence = 0;
|
|
testResult.startTime = millis();
|
|
testResult.framesSent = 0;
|
|
testResult.framesReceived = 0;
|
|
testResult.framesLost = 0;
|
|
testResult.errors = 0;
|
|
|
|
uint8_t originalMode = getCANMode();
|
|
setCANMode(2);
|
|
delay(100);
|
|
|
|
expectedSequence = 0;
|
|
|
|
while (testConfig.running && sequence < testConfig.frameCount) {
|
|
CanFrame frame;
|
|
generateTestFrame(sequence, frame);
|
|
|
|
if (sendCANFrame(frame.id, frame.data, frame.len, testConfig.useFD)) {
|
|
testResult.framesSent++;
|
|
sequence++;
|
|
} else {
|
|
testResult.errors++;
|
|
}
|
|
|
|
if (testConfig.intervalUs > 0) {
|
|
delayMicroseconds(testConfig.intervalUs);
|
|
}
|
|
|
|
while (canController.available()) {
|
|
CANFDMessage rxMsg;
|
|
if (canController.receive(rxMsg)) {
|
|
CanFrame rxFrame;
|
|
rxFrame.id = rxMsg.id;
|
|
rxFrame.len = rxMsg.len;
|
|
memcpy(rxFrame.data, rxMsg.data, rxMsg.len);
|
|
|
|
if (rxFrame.id == testConfig.canId) {
|
|
if (validateTestFrame(rxFrame, expectedSequence)) {
|
|
testResult.framesReceived++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delay(100);
|
|
|
|
while (canController.available()) {
|
|
CANFDMessage rxMsg;
|
|
if (canController.receive(rxMsg)) {
|
|
CanFrame rxFrame;
|
|
rxFrame.id = rxMsg.id;
|
|
rxFrame.len = rxMsg.len;
|
|
memcpy(rxFrame.data, rxMsg.data, rxMsg.len);
|
|
|
|
if (rxFrame.id == testConfig.canId) {
|
|
if (validateTestFrame(rxFrame, expectedSequence)) {
|
|
testResult.framesReceived++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
testResult.endTime = millis();
|
|
testResult.durationMs = testResult.endTime - testResult.startTime;
|
|
|
|
if (testResult.durationMs > 0) {
|
|
testResult.frameRate = (float)testResult.framesSent / ((float)testResult.durationMs / 1000.0f);
|
|
}
|
|
|
|
if (testResult.framesSent > 0) {
|
|
testResult.lossRate = (float)testResult.framesLost / (float)testResult.framesSent * 100.0f;
|
|
}
|
|
|
|
testResult.passed = (testResult.framesLost == 0 && testResult.framesSent == testResult.framesReceived);
|
|
|
|
setCANMode(originalMode);
|
|
testConfig.running = false;
|
|
testConfig.mode = TEST_MODE_IDLE;
|
|
|
|
Serial.printf("Test completed: Sent=%d, Received=%d, Lost=%d, Rate=%.1f fps, Loss=%.2f%%\n",
|
|
testResult.framesSent, testResult.framesReceived, testResult.framesLost,
|
|
testResult.frameRate, testResult.lossRate);
|
|
|
|
vTaskDelete(NULL);
|
|
testTaskHandle = NULL;
|
|
}
|
|
|
|
bool startLoopbackTest(uint32_t frameCount, uint32_t intervalUs) {
|
|
if (testConfig.running) {
|
|
return false;
|
|
}
|
|
|
|
testConfig.mode = TEST_MODE_LOOPBACK;
|
|
testConfig.frameCount = frameCount;
|
|
testConfig.intervalUs = intervalUs;
|
|
testConfig.canId = 0x100;
|
|
testConfig.dataLen = 8;
|
|
testConfig.useFD = false;
|
|
testConfig.running = true;
|
|
|
|
Serial.printf("Starting loopback test: %d frames, %d us interval\n", frameCount, intervalUs);
|
|
|
|
xTaskCreatePinnedToCore(
|
|
testTxTask,
|
|
"TEST_TX",
|
|
4096,
|
|
NULL,
|
|
6,
|
|
&testTaskHandle,
|
|
0
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool startStressTest(uint32_t frameCount, uint8_t dataLen, bool useFD) {
|
|
if (testConfig.running) {
|
|
return false;
|
|
}
|
|
|
|
testConfig.mode = TEST_MODE_STRESS;
|
|
testConfig.frameCount = frameCount;
|
|
testConfig.intervalUs = 0;
|
|
testConfig.canId = 0x200;
|
|
testConfig.dataLen = dataLen;
|
|
testConfig.useFD = useFD;
|
|
testConfig.running = true;
|
|
|
|
Serial.printf("Starting stress test: %d frames, %d bytes, FD=%s\n",
|
|
frameCount, dataLen, useFD ? "true" : "false");
|
|
|
|
xTaskCreatePinnedToCore(
|
|
testTxTask,
|
|
"TEST_TX",
|
|
4096,
|
|
NULL,
|
|
6,
|
|
&testTaskHandle,
|
|
0
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool startSequenceTest(uint32_t frameCount, uint32_t canId) {
|
|
if (testConfig.running) {
|
|
return false;
|
|
}
|
|
|
|
testConfig.mode = TEST_MODE_SEQUENCE;
|
|
testConfig.frameCount = frameCount;
|
|
testConfig.intervalUs = 1000;
|
|
testConfig.canId = canId;
|
|
testConfig.dataLen = 64;
|
|
testConfig.useFD = true;
|
|
testConfig.running = true;
|
|
|
|
Serial.printf("Starting sequence test: %d frames, ID=0x%X\n", frameCount, canId);
|
|
|
|
xTaskCreatePinnedToCore(
|
|
testTxTask,
|
|
"TEST_TX",
|
|
4096,
|
|
NULL,
|
|
6,
|
|
&testTaskHandle,
|
|
0
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
void stopTest() {
|
|
if (testConfig.running) {
|
|
testConfig.running = false;
|
|
if (testTaskHandle != NULL) {
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateTest() {
|
|
}
|
|
|
|
bool isTestRunning() {
|
|
return testConfig.running;
|
|
}
|
|
|
|
TestMode getTestMode() {
|
|
return testConfig.mode;
|
|
}
|
|
|
|
TestResult getTestResult() {
|
|
return testResult;
|
|
}
|
|
|
|
void getTestResultJSON(char* buffer, size_t bufferSize) {
|
|
snprintf(buffer, bufferSize,
|
|
"{\"running\":%s,\"mode\":%d,\"result\":{"
|
|
"\"framesSent\":%d,\"framesReceived\":%d,\"framesLost\":%d,"
|
|
"\"errors\":%d,\"durationMs\":%d,\"frameRate\":%.1f,"
|
|
"\"lossRate\":%.2f,\"passed\":%s}}",
|
|
testConfig.running ? "true" : "false",
|
|
testConfig.mode,
|
|
testResult.framesSent,
|
|
testResult.framesReceived,
|
|
testResult.framesLost,
|
|
testResult.errors,
|
|
testResult.durationMs,
|
|
testResult.frameRate,
|
|
testResult.lossRate,
|
|
testResult.passed ? "true" : "false");
|
|
}
|