|
#include "isobus/isobus/can_transport_protocol.hpp" |
|
#include "isobus/isobus/can_message.hpp" |
|
#include "isobus/isobus/can_network_configuration.hpp" |
|
#include "isobus/isobus/can_internal_control_function.hpp" |
|
#include "isobus/isobus/can_partnered_control_function.hpp" |
|
#include "isobus/isobus/can_control_function.hpp" |
|
#include "isobus/isobus/can_identifier.hpp" |
|
#include "isobus/isobus/can_constants.hpp" |
|
#include "isobus/isobus/can_NAME.hpp" |
|
#include "isobus/isobus/can_callbacks.hpp" |
|
|
|
#include <iostream> |
|
#include <vector> |
|
#include <chrono> |
|
#include <cstring> |
|
#include <iomanip> |
|
#include <memory> |
|
#include <functional> |
|
|
|
using namespace isobus; |
|
|
|
// Test configuration |
|
const int NUM_ITERATIONS = 1000; |
|
|
|
// Helper to create test data |
|
std::vector<std::uint8_t> createTestData(size_t size) { |
|
std::vector<std::uint8_t> data(size); |
|
for (size_t i = 0; i < size; i++) { |
|
data[i] = static_cast<std::uint8_t>(i & 0xFF); |
|
} |
|
return data; |
|
} |
|
|
|
// Helper to verify received data |
|
bool verifyData(const std::vector<std::uint8_t>& expected, const std::vector<std::uint8_t>& actual) { |
|
if (expected.size() != actual.size()) { |
|
std::cout << " [FAIL] Size mismatch: expected " << expected.size() << ", got " << actual.size() << std::endl; |
|
return false; |
|
} |
|
|
|
for (size_t i = 0; i < expected.size(); i++) { |
|
if (expected[i] != actual[i]) { |
|
std::cout << " [FAIL] Data mismatch at byte " << i << std::endl; |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
// Test TP with real library |
|
void testTPWithLibrary(size_t messageSize, int iterations) { |
|
std::cout << "\nTesting TP with AgIsoStack library (" << messageSize << " bytes, " << iterations << " iterations)..." << std::endl; |
|
|
|
// Create test data |
|
auto testData = createTestData(messageSize); |
|
|
|
// Track received messages |
|
std::vector<std::uint8_t> receivedData; |
|
bool messageReceived = false; |
|
|
|
// Receive callback |
|
auto receiveCallback = [&](const CANMessage& message) { |
|
messageReceived = true; |
|
receivedData = message.get_data(); |
|
}; |
|
|
|
// Send callback (no-op for this test, uses CANDataSpan) |
|
CANMessageFrameCallback sendCallback = [](std::uint32_t, CANDataSpan, std::shared_ptr<InternalControlFunction>, std::shared_ptr<ControlFunction>, CANIdentifier::CANPriority) { |
|
return true; |
|
}; |
|
|
|
// Create network configuration |
|
CANNetworkConfiguration config; |
|
|
|
// Create TP manager |
|
TransportProtocolManager tpManager(sendCallback, receiveCallback, &config); |
|
|
|
// Create mock control function (use proper constructor with CANPort) |
|
NAME sourceName(0); |
|
auto source = std::make_shared<ControlFunction>(sourceName, 0x01, 0, ControlFunction::Type::External); |
|
|
|
// Calculate packets needed |
|
const std::uint8_t PROTOCOL_BYTES_PER_FRAME = 7; |
|
std::uint16_t numPackets = (messageSize + PROTOCOL_BYTES_PER_FRAME - 1) / PROTOCOL_BYTES_PER_FRAME; |
|
|
|
// Performance timing |
|
auto start = std::chrono::high_resolution_clock::now(); |
|
|
|
for (int iter = 0; iter < iterations; iter++) { |
|
messageReceived = false; |
|
receivedData.clear(); |
|
|
|
// 1. Send BAM (Broadcast Announce Message) |
|
std::vector<std::uint8_t> bamData = { |
|
32, // BAM mux |
|
static_cast<std::uint8_t>(messageSize & 0xFF), // Length LSB |
|
static_cast<std::uint8_t>((messageSize >> 8) & 0xFF), // Length MSB |
|
static_cast<std::uint8_t>(numPackets), // Total packets |
|
0xFF, // Reserved |
|
0xEC, // PGN LSB (using 0xFEEC as test PGN) |
|
0xFE, // PGN middle |
|
0x00 // PGN MSB |
|
}; |
|
|
|
CANIdentifier bamId(CANIdentifier::Type::Extended, 0xEC00, CANIdentifier::CANPriority::PriorityDefault6, 0xFF, 0x01); |
|
CANMessage bamMessage(CANMessage::Type::Receive, bamId, bamData, source, nullptr, 0); |
|
|
|
tpManager.process_message(bamMessage); |
|
|
|
// 2. Send data transfer packets (THIS IS WHERE THE OPTIMIZED memcpy CODE RUNS) |
|
std::uint32_t dataOffset = 0; |
|
for (std::uint16_t seq = 1; seq <= numPackets; seq++) { |
|
std::vector<std::uint8_t> frameData(8, 0xFF); |
|
frameData[0] = static_cast<std::uint8_t>(seq); |
|
|
|
std::size_t bytesToCopy = std::min( |
|
static_cast<std::size_t>(PROTOCOL_BYTES_PER_FRAME), |
|
messageSize - dataOffset |
|
); |
|
|
|
std::memcpy(&frameData[1], &testData[dataOffset], bytesToCopy); |
|
dataOffset += bytesToCopy; |
|
|
|
CANIdentifier dtId(CANIdentifier::Type::Extended, 0xEB00, CANIdentifier::CANPriority::PriorityDefault6, 0xFF, 0x01); |
|
CANMessage dtMessage(CANMessage::Type::Receive, dtId, frameData, source, nullptr, 0); |
|
|
|
// THIS CALL TRIGGERS THE OPTIMIZED CODE PATH! |
|
tpManager.process_message(dtMessage); |
|
} |
|
|
|
tpManager.update(); |
|
} |
|
|
|
auto end = std::chrono::high_resolution_clock::now(); |
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); |
|
|
|
// Verify last iteration |
|
if (!messageReceived) { |
|
std::cout << " [FAIL] Message not received" << std::endl; |
|
return; |
|
} |
|
|
|
if (!verifyData(testData, receivedData)) { |
|
std::cout << " [FAIL] Data verification failed" << std::endl; |
|
return; |
|
} |
|
|
|
std::cout << " [PASS] Data integrity verified" << std::endl; |
|
|
|
// Performance metrics |
|
double avgTimePerMessage = static_cast<double>(duration) / iterations; |
|
double packetsPerSecond = (iterations * numPackets * 1000000.0) / duration; |
|
double throughputMBps = (iterations * messageSize * 1000000.0) / (duration * 1024.0 * 1024.0); |
|
|
|
std::cout << " Performance:" << std::endl; |
|
std::cout << " Average time per message: " << std::fixed << std::setprecision(2) |
|
<< avgTimePerMessage << " µs" << std::endl; |
|
std::cout << " Packets per second: " << std::fixed << std::setprecision(0) |
|
<< packetsPerSecond << std::endl; |
|
std::cout << " Throughput: " << std::fixed << std::setprecision(2) |
|
<< throughputMBps << " MB/s" << std::endl; |
|
} |
|
|
|
|
|
int main() { |
|
std::cout << "========================================" << std::endl; |
|
std::cout << "AgIsoStack TP Optimization Integration Test" << std::endl; |
|
std::cout << "Testing ACTUAL library code with hybrid optimization" << std::endl; |
|
std::cout << "========================================" << std::endl; |
|
|
|
std::cout << "\n============================================" << std::endl; |
|
std::cout << "TRANSPORT PROTOCOL (TP) TESTS" << std::endl; |
|
std::cout << "============================================" << std::endl; |
|
|
|
testTPWithLibrary(100, NUM_ITERATIONS); |
|
testTPWithLibrary(1000, NUM_ITERATIONS); |
|
testTPWithLibrary(1785, NUM_ITERATIONS); |
|
|
|
std::cout << "\n========================================" << std::endl; |
|
std::cout << "INTEGRATION TEST COMPLETE" << std::endl; |
|
std::cout << "========================================" << std::endl; |
|
std::cout << "\nThis test uses the ACTUAL AgIsoStack library code." << std::endl; |
|
std::cout << "Performance differences reflect real optimization impact." << std::endl; |
|
std::cout << "\nNote: ETP uses identical optimization code (same 7-byte frame logic)." << std::endl; |
|
std::cout << "TP results demonstrate the performance gains for all protocols." << std::endl; |
|
std::cout << "\nTo compare:" << std::endl; |
|
std::cout << "1. Build with GIT_TAG=main -> save as baseline-integration.txt" << std::endl; |
|
std::cout << "2. Build with GIT_TAG=TP-Optimization -> save as optimized-integration.txt" << std::endl; |
|
std::cout << "3. Compare the timing results" << std::endl; |
|
std::cout << "========================================" << std::endl; |
|
|
|
return 0; |
|
} |