LCOV - code coverage report
Current view: top level - tpglibs/test/apps - test_tpg_processor_app.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 187 0
Test Date: 2025-12-21 13:07:08 Functions: 0.0 % 9 0

            Line data    Source code
       1              : /**
       2              :  * @file test_tpg_processor_app.cxx
       3              :  *
       4              :  * @brief TPG Processor Test Application - Processes binary data through configured processors
       5              :  *
       6              :  * @copyright This is part of the DUNE DAQ Software Suite, copyright 2020.
       7              :  * Licensing/copyright details are in the COPYING file that you should have
       8              :  * received with this code.
       9              :  */
      10              : 
      11              : #include "tpglibs/testapp/reader/BinarySignalReader.hpp"
      12              : #include "tpglibs/AVXFactory.hpp"
      13              : #include "tpglibs/NaiveFactory.hpp"
      14              : #include "tpglibs/AbstractProcessor.hpp"
      15              : #include <iostream>
      16              : #include <fstream>
      17              : #include <vector>
      18              : #include <nlohmann/json.hpp>
      19              : #include <cstdint>
      20              : #include <cstring>
      21              : #include <algorithm>
      22              : 
      23              : struct BinaryFileHeader {
      24              :     uint32_t magic_number;
      25              :     uint32_t version;
      26              :     uint32_t reserved;
      27              : };
      28              : 
      29            0 : bool validate_binary_header(std::ifstream& file) {
      30            0 :     BinaryFileHeader header;
      31            0 :     file.read(reinterpret_cast<char*>(&header), sizeof(BinaryFileHeader));
      32              :     
      33            0 :     if (header.magic_number != 0x54504754) {  // "TPGT"
      34            0 :         std::cerr << "ERROR: Invalid input file magic number: 0x" 
      35            0 :                   << std::hex << header.magic_number << std::dec << std::endl;
      36            0 :         return false;
      37              :     }
      38            0 :     if (header.version != 0x010004) {  // 1.0.4 in hex
      39            0 :         std::cerr << "ERROR: Unsupported input file version: 0x" 
      40            0 :                   << std::hex << header.version << std::dec << std::endl;
      41            0 :         return false;
      42              :     }
      43              :     return true;
      44              : }
      45              : 
      46              : 
      47              : std::shared_ptr<tpglibs::AbstractProcessor<std::array<int16_t, 16>>> 
      48            0 : create_naive_processor(const std::string& processor_name) {
      49            0 :     auto factory = tpglibs::NaiveFactory::get_instance();
      50            0 :     auto processor = factory->create_processor(processor_name);
      51            0 :     return processor;
      52            0 : }
      53              : 
      54              : std::shared_ptr<tpglibs::AVXProcessor>
      55            0 : create_avx_processor(const std::string& processor_name) {
      56            0 :     auto factory = tpglibs::AVXFactory::get_instance();
      57            0 :     auto processor = factory->create_processor(processor_name);
      58            0 :     return processor;
      59            0 : }
      60              : 
      61            0 : std::array<int16_t, 16> convert_vector_to_array(const std::vector<int16_t>& vec) {
      62            0 :     std::array<int16_t, 16> result;
      63            0 :     std::fill(result.begin(), result.end(), 0);
      64            0 :     std::copy(vec.begin(), vec.begin() + std::min(vec.size(), size_t(16)), result.begin());
      65            0 :     return result;
      66              : }
      67              : 
      68            0 : std::vector<int16_t> convert_array_to_vector(const std::array<int16_t, 16>& arr) {
      69            0 :     return std::vector<int16_t>(arr.begin(), arr.end());
      70              : }
      71              : 
      72            0 : __m256i convert_array_i16x16_to_m256(const std::array<int16_t, 16>& arr) {
      73            0 :     return _mm256_lddqu_si256(reinterpret_cast<const __m256i*>(arr.data()));
      74              : }
      75              : 
      76            0 : std::array<int16_t, 16> convert_m256_to_array_i16x16(const __m256i& avx_val) {
      77            0 :     std::array<int16_t, 16> result;
      78            0 :     _mm256_storeu_si256(reinterpret_cast<__m256i*>(result.data()), avx_val);
      79            0 :     return result;
      80              : }
      81              : 
      82            0 : void print_usage(const char* program_name) {
      83            0 :     std::cerr << "Usage: " << program_name 
      84            0 :               << " <input_file> <config_file> <validation_file>" << std::endl;
      85            0 :     std::cerr << "Example: " << program_name 
      86            0 :               << " test_input.bin config.json validation.bin" << std::endl;
      87            0 : }
      88              : 
      89            0 : int main(int argc, char* argv[]) {
      90            0 :     if (argc != 4) {
      91            0 :         print_usage(argv[0]);
      92            0 :         return 1;
      93              :     }
      94              :     
      95            0 :     std::string input_file = argv[1];
      96            0 :     std::string config_file = argv[2];
      97            0 :     std::string validation_file = argv[3];
      98              :     
      99              :     // Load configuration
     100            0 :     std::ifstream config_stream(config_file);
     101            0 :     if (!config_stream.is_open()) {
     102            0 :         std::cerr << "ERROR: Failed to open config file: " << config_file << std::endl;
     103              :         return 1;
     104              :     }
     105              :     
     106            0 :     nlohmann::json config;
     107            0 :     try {
     108            0 :         config_stream >> config;
     109            0 :     } catch (const std::exception& e) {
     110            0 :         std::cerr << "ERROR: Failed to parse config file: " << e.what() << std::endl;
     111            0 :         return 1;
     112            0 :     }
     113              :     
     114              :     // Validate input file
     115            0 :     std::ifstream input_stream(input_file, std::ios::binary);
     116            0 :     if (!input_stream.is_open()) {
     117            0 :         std::cerr << "ERROR: Failed to open input file: " << input_file << std::endl;
     118              :         return 1;
     119              :     }
     120              :     
     121            0 :     if (!validate_binary_header(input_stream)) {
     122              :         return 1;
     123              :     }
     124              :     
     125              :     // Validate validation file
     126            0 :     std::ifstream validation_stream(validation_file, std::ios::binary);
     127            0 :     if (!validation_stream.is_open()) {
     128            0 :         std::cerr << "ERROR: Failed to open validation file: " << validation_file << std::endl;
     129              :         return 1;
     130              :     }
     131              :     
     132            0 :     if (!validate_binary_header(validation_stream)) {
     133              :         return 1;
     134              :     }
     135              :     
     136              :     // Test config validation block
     137            0 :     if (!config.contains("test_config") || !config["test_config"].is_object()) {
     138            0 :         std::cerr << "ERROR: Missing test_config section in config" << std::endl;
     139              :         return 1;
     140              :     }
     141            0 :     const auto& test_config = config["test_config"];
     142              : 
     143            0 :     if (!test_config.contains("processor_name") || !test_config["processor_name"].is_string()) {
     144            0 :         std::cerr << "ERROR: test_config.processor_name must be a string" << std::endl;
     145              :         return 1;
     146              :     }
     147            0 :     std::string processor_name = test_config["processor_name"].get<std::string>();
     148              : 
     149            0 :     if (!test_config.contains("samples_per_time_step") || !test_config["samples_per_time_step"].is_number_integer()) {
     150            0 :         std::cerr << "ERROR: test_config.samples_per_time_step must be an integer" << std::endl;
     151              :         return 1;
     152              :     }
     153            0 :     int samples_per_time_step = test_config["samples_per_time_step"].get<int>();
     154              : 
     155            0 :     auto validation_steps = test_config.value("validation_steps", nlohmann::json::array());
     156            0 :     if (!validation_steps.is_array()) {
     157            0 :         std::cerr << "ERROR: test_config.validation_steps must be an array if provided" << std::endl;
     158              :         return 1;
     159              :     }
     160              : 
     161            0 :     int max_steps = 0;
     162            0 :     if (test_config.contains("max_steps") && test_config["max_steps"].is_number_integer()) {
     163            0 :         max_steps = test_config["max_steps"].get<int>();
     164            0 :         if (max_steps <= 0) {
     165            0 :             std::cerr << "ERROR: test_config.max_steps must be a positive integer" << std::endl;
     166              :             return 1;
     167              :         }
     168              :     } else {
     169            0 :         if (validation_steps.empty()) {
     170            0 :             std::cerr << "ERROR: Either test_config.max_steps or non-empty test_config.validation_steps must be provided" << std::endl;
     171            0 :             return 1;
     172              :         }
     173            0 :         int derived_max = 0;
     174            0 :         for (const auto& v : validation_steps) {
     175            0 :             if (!v.is_number_integer()) {
     176            0 :                 std::cerr << "ERROR: validation_steps must contain integers" << std::endl;
     177            0 :                 return 1;
     178              :             }
     179            0 :             derived_max = std::max(derived_max, v.get<int>());
     180              :         }
     181            0 :         max_steps = derived_max + 1;
     182              :     }
     183              :     
     184              :     // Create processor based on name
     185            0 :     std::shared_ptr<tpglibs::AVXProcessor> avx_processor;
     186            0 :     std::shared_ptr<tpglibs::AbstractProcessor<std::array<int16_t, 16>>> naive_processor;
     187            0 :     bool is_avx = (processor_name.find("AVX") != std::string::npos);
     188              :     
     189            0 :     try {
     190            0 :         if (is_avx) {
     191            0 :             avx_processor = create_avx_processor(processor_name);
     192              :         } else {
     193            0 :             naive_processor = create_naive_processor(processor_name);
     194              :         }
     195            0 :     } catch (const std::exception& e) {
     196            0 :         std::cerr << "ERROR: Failed to create processor: " << e.what() << std::endl;
     197            0 :         return 1;
     198            0 :     }
     199              :     
     200              :     // Configure processor
     201            0 :     int16_t plane_numbers[16] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2};
     202            0 :     nlohmann::json processor_config = config["processor_config"];
     203              :     
     204            0 :     if (is_avx) {
     205            0 :         avx_processor->configure(processor_config, plane_numbers);
     206              :     } else {
     207            0 :         naive_processor->configure(processor_config, plane_numbers);
     208              :     }
     209              :     
     210              :     // Read binary data
     211            0 :     tpglibs::testapp::BinarySignalReader<int16_t> reader(input_file);
     212              :     // Skip the header (12 bytes)
     213            0 :     reader.seekg(12);
     214              :     
     215              :     // Read validation data only if validation_steps is provided
     216            0 :     std::vector<std::vector<int16_t>> validation_data;
     217            0 :     bool has_validation = !validation_steps.empty();
     218              :     
     219            0 :     if (has_validation) {
     220              :         // Read expected outputs by seeking to the correct step offset in the validation file
     221            0 :         validation_data.resize(validation_steps.size());
     222            0 :         const std::streamsize step_bytes = static_cast<std::streamsize>(samples_per_time_step * sizeof(int16_t));
     223            0 :         const std::streamoff header_size = static_cast<std::streamoff>(sizeof(BinaryFileHeader));
     224              : 
     225            0 :         for (size_t i = 0; i < validation_steps.size(); ++i) {
     226            0 :             validation_data[i].resize(samples_per_time_step);
     227            0 :             int target_step = validation_steps.at(i).get<int>();
     228            0 :             std::streamoff offset = header_size + static_cast<std::streamoff>(target_step) * step_bytes;
     229              :             
     230            0 :             validation_stream.clear();
     231            0 :             validation_stream.seekg(offset, std::ios::beg);
     232            0 :             validation_stream.read(reinterpret_cast<char*>(validation_data[i].data()), step_bytes);
     233              :             
     234            0 :             if (validation_stream.gcount() < step_bytes) {
     235            0 :                 std::cout << "WARNING: Validation file does not contain data for step " << target_step 
     236            0 :                           << ". Loaded " << i << "/" << validation_steps.size() << " validation records." << std::endl;
     237            0 :                 validation_data.resize(i);
     238              :                 break;
     239              :             }
     240              :         }
     241              :     }
     242              :     
     243              :     // Process data step by step
     244            0 :     int step = 0;
     245            0 :     size_t validation_index = 0;
     246            0 :     bool all_passed = true;
     247            0 :     size_t num_steps_processed = 0;
     248              :     
     249            0 :     std::cout << "Starting processing..." << std::endl;
     250            0 :     std::cout << "Processor: " << processor_name << std::endl;
     251            0 :     std::cout << "Max steps: " << max_steps << std::endl;
     252            0 :     if (has_validation) {
     253            0 :         std::cout << "Validation steps: ";
     254            0 :         for (auto step_num : validation_steps) {
     255            0 :             std::cout << step_num << " ";
     256            0 :         }
     257            0 :         std::cout << std::endl;
     258              :     } else {
     259            0 :         std::cout << "Validation: Disabled (no validation_steps provided)" << std::endl;
     260              :     }
     261            0 :     std::cout << std::endl;
     262              :     
     263            0 :     while (step < max_steps && !reader.eof()) {
     264              :         // Read one time step of data
     265            0 :         auto time_step_data = reader.next(samples_per_time_step);
     266            0 :         if (time_step_data.empty()) break;
     267              :         
     268              :         // Ensure we have exactly 16 samples
     269            0 :         time_step_data.resize(16, 0);
     270              :         
     271              :         // Convert to array
     272            0 :         std::array<int16_t, 16> input_array = convert_vector_to_array(time_step_data);
     273              :         
     274              :         // Process the time step
     275            0 :         std::array<int16_t, 16> result;
     276            0 :         if (is_avx) {
     277            0 :             __m256i input_avx = convert_array_i16x16_to_m256(input_array);
     278            0 :             __m256i result_avx = avx_processor->process(input_avx);
     279            0 :             result = convert_m256_to_array_i16x16(result_avx);
     280              :         } else {
     281            0 :             result = naive_processor->process(input_array);
     282              :         }
     283              :         
     284              :         // Check if this is a validation step
     285            0 :         if (validation_index < validation_data.size() &&
     286            0 :             step == validation_steps.at(validation_index).get<int>()) {
     287            0 :             const std::vector<int16_t>& expected = validation_data[validation_index];
     288              : 
     289              :             // Find first mismatch using std::mismatch for clarity
     290            0 :             auto mm = std::mismatch(result.begin(), result.end(), expected.begin());
     291            0 :             bool passed = (mm.first == result.end());
     292              : 
     293            0 :             if (!passed) {
     294            0 :                 if (all_passed) {
     295            0 :                     int idx = static_cast<int>(std::distance(result.begin(), mm.first));
     296            0 :                     std::cout << "Validation step " << step << " FAILED:" << std::endl;
     297            0 :                     std::cout << "  Expected: ";
     298            0 :                     for (int j = 0; j < 16; ++j) {
     299            0 :                         std::cout << expected[j] << " ";
     300              :                     }
     301            0 :                     std::cout << std::endl;
     302            0 :                     std::cout << "  Actual:   ";
     303            0 :                     for (int j = 0; j < 16; ++j) {
     304            0 :                         std::cout << result[j] << " ";
     305              :                     }
     306            0 :                     std::cout << std::endl;
     307            0 :                     std::cout << "  First mismatch at index " << idx << ": expected "
     308            0 :                               << expected[idx] << ", got " << result[idx] << std::endl;
     309              :                 }
     310              :                 all_passed = false;
     311              :             } else {
     312            0 :                 std::cout << "Validation step " << step << " PASSED" << std::endl;
     313              :             }
     314              : 
     315            0 :             validation_index++;
     316              :         }
     317              :         
     318            0 :         step++;
     319            0 :         num_steps_processed++;
     320            0 :     }
     321              : 
     322              :     // Warn if input ended before completing all loaded validation steps
     323            0 :     if (has_validation && validation_index < validation_data.size() && reader.eof()) {
     324            0 :         std::cout << "WARNING: Reached end of input before completing all validation steps. Completed "
     325            0 :                   << validation_index << "/" << validation_data.size() << " validation steps." << std::endl;
     326              :     }
     327              :     
     328              :     // Report final results
     329            0 :     std::cout << std::endl << "Processing completed." << std::endl;
     330            0 :     std::cout << "Steps processed: " << num_steps_processed << std::endl;
     331            0 :     if (has_validation) {
     332            0 :         std::cout << "Validation results: " << (all_passed ? "PASS" : "FAIL") << std::endl;
     333            0 :         return all_passed ? 0 : 1;
     334              :     } else {
     335            0 :         std::cout << "Validation: Not performed" << std::endl;
     336              :         return 0;
     337              :     }
     338            0 : }
     339              : 
        

Generated by: LCOV version 2.0-1