diff --git a/CMakeLists.txt b/CMakeLists.txt index d8d72c5..65aa471 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.12) -project(trigger VERSION 2.1.1) +project(trigger VERSION 2.1.2) find_package(daq-cmake REQUIRED) diff --git a/integtest/README.md b/integtest/README.md index d8fda3e..68e24cf 100644 --- a/integtest/README.md +++ b/integtest/README.md @@ -9,3 +9,4 @@ pytest -s td_leakage_between_runs_test.py [--nanorc-option partition-number 2] [ For reference, here are the ideas behind the existing tests: * `td_leakage_between_runs_test.py` - tests whether TriggerDecision messages received by the DFO have the correct run number in certain special conditions +* `change_rate_test.py` - Tests whether the change-rate command properly changes the rate of the RTCM and not the FakeHSI diff --git a/integtest/change_rate_test.py b/integtest/change_rate_test.py new file mode 100644 index 0000000..1767b9e --- /dev/null +++ b/integtest/change_rate_test.py @@ -0,0 +1,169 @@ +import pytest +import copy + +import integrationtest.data_file_checks as data_file_checks +import integrationtest.log_file_checks as log_file_checks +import integrationtest.data_classes as data_classes + +pytest_plugins = "integrationtest.integrationtest_drunc" + +# Values that help determine the running conditions +number_of_data_producers = 1 +data_rate_slowdown_factor = 1 # 10 for ProtoWIB/DuneWIB +run_duration = 200 # seconds (has to be this long for 0.01Hz rate to show that it is working) +readout_window_time_before = 1000 +readout_window_time_after = 1001 + +# Default values for validation parameters +expected_number_of_data_files = 4 +check_for_logfile_errors = True +expected_event_count = 2 +expected_event_count_tolerance = 1 +wib1_frag_hsi_trig_params = { + "fragment_type_description": "WIB", + "fragment_type": "ProtoWIB", + "hdf5_source_subsystem": "Detector_Readout", + "expected_fragment_count": number_of_data_producers, + "min_size_bytes": 37656, + "max_size_bytes": 37656, +} +wib2_frag_params = { + "fragment_type_description": "WIB2", + "fragment_type": "WIB", + "hdf5_source_subsystem": "Detector_Readout", + "expected_fragment_count": number_of_data_producers, + "min_size_bytes": 29808, + "max_size_bytes": 30280, +} +wibeth_frag_params = { + "fragment_type_description": "WIBEth", + "fragment_type": "WIBEth", + "hdf5_source_subsystem": "Detector_Readout", + "expected_fragment_count": number_of_data_producers, + "min_size_bytes": 7272, + "max_size_bytes": 14472, +} +triggercandidate_frag_params = { + "fragment_type_description": "Trigger Candidate", + "fragment_type": "Trigger_Candidate", + "hdf5_source_subsystem": "Trigger", + "expected_fragment_count": 1, + "min_size_bytes": 128, + "max_size_bytes": 216, +} +hsi_frag_params = { + "fragment_type_description": "HSI", + "fragment_type": "Hardware_Signal", + "hdf5_source_subsystem": "HW_Signals_Interface", + "expected_fragment_count": 0, + "min_size_bytes": 72, + "max_size_bytes": 100, +} +ignored_logfile_problems = { + "connectionservice": [ + "Searching for connections matching uid_regex and data_type Unknown" + ], + "-controller": [ + "Worker with pid \\d+ was terminated due to signal 1", + "Connection '.*' not found on the application registry", + ], + "local-connection-server": [ + "errorlog: -", + "Worker with pid \\d+ was terminated due to signal 1", + ], + "log_.*_changerate_": ["connect: Connection refused"], +} + +# The next three variable declarations *must* be present as globals in the test +# file. They're read by the "fixtures" in conftest.py to determine how +# to run the config generation and nanorc + +# The arguments to pass to the config generator, excluding the json +# output directory (the test framework handles that) + +object_databases = ["config/daqsystemtest/integrationtest-objects.data.xml"] + +conf_dict = data_classes.drunc_config() +conf_dict.dro_map_config.n_streams = number_of_data_producers +conf_dict.op_env = "integtest" +conf_dict.session = "changerate" +conf_dict.tpg_enabled = False + +conf_dict.config_substitutions.append( + data_classes.config_substitution( + obj_id=conf_dict.session, + obj_class="Session", + updates={"data_rate_slowdown_factor": data_rate_slowdown_factor}, + ) +) +conf_dict.config_substitutions.append( + data_classes.config_substitution(obj_class="LatencyBuffer", updates={"size": 50000}) +) + +conf_dict.config_substitutions.append( + data_classes.config_substitution( + obj_class="RandomTCMakerConf", + updates={"trigger_rate_hz": 1.0}, + ) +) + +conf_dict.config_substitutions.append( + data_classes.config_substitution( + obj_class="TCReadoutMap", + updates={ + "time_before": readout_window_time_before, + "time_after": readout_window_time_after, + }, + ) +) + +confgen_arguments = {"MinimalSystem": conf_dict} +# The commands to run in nanorc, as a list +nanorc_command_list = ( + "boot conf".split() + + " start 101 wait 1 enable-triggers wait 2 disable-triggers wait 2 drain-dataflow stop-trigger-sources stop wait 2".split() + + " start 102 wait 1 change-rate 2.0 enable-triggers wait 1 disable-triggers wait 2 drain-dataflow stop-trigger-sources stop wait 2".split() + + " start 103 wait 1 change-rate 0.1 enable-triggers wait 20 disable-triggers wait 2 drain-dataflow stop-trigger-sources stop wait 2".split() + + " start 104 wait 1 change-rate 0.01 enable-triggers wait 200 disable-triggers wait 2 drain-dataflow stop-trigger-sources stop wait 2".split() ++ "scrap terminate".split() +) + +# The tests themselves + + +def test_nanorc_success(run_nanorc): + # Check that nanorc completed correctly + assert run_nanorc.completed_process.returncode == 0 + + +def test_log_files(run_nanorc): + if check_for_logfile_errors: + # Check that there are no warnings or errors in the log files + assert log_file_checks.logs_are_error_free( + run_nanorc.log_files, True, True, ignored_logfile_problems + ) + + +def test_data_files(run_nanorc): + # Run some tests on the output data file + assert len(run_nanorc.data_files) == expected_number_of_data_files + + fragment_check_list = [triggercandidate_frag_params, hsi_frag_params] + # fragment_check_list.append(wib1_frag_hsi_trig_params) # ProtoWIB + # fragment_check_list.append(wib2_frag_params) # DuneWIB + fragment_check_list.append(wibeth_frag_params) # WIBEth + + for idx in range(len(run_nanorc.data_files)): + data_file = data_file_checks.DataFile(run_nanorc.data_files[idx]) + assert data_file_checks.sanity_check(data_file) + assert data_file_checks.check_file_attributes(data_file) + assert data_file_checks.check_event_count( + data_file, expected_event_count, expected_event_count_tolerance + ) + for jdx in range(len(fragment_check_list)): + assert data_file_checks.check_fragment_count( + data_file, fragment_check_list[jdx] + ) + assert data_file_checks.check_fragment_sizes( + data_file, fragment_check_list[jdx] + ) diff --git a/plugins/RandomTCMakerModule.cpp b/plugins/RandomTCMakerModule.cpp index 6acadaf..439dc71 100644 --- a/plugins/RandomTCMakerModule.cpp +++ b/plugins/RandomTCMakerModule.cpp @@ -198,19 +198,19 @@ RandomTCMakerModule::create_candidate(dfmessages::timestamp_t timestamp) return candidate; } -int +uint64_t RandomTCMakerModule::get_interval(std::mt19937& gen) { std::string time_distribution = m_conf->get_time_distribution(); - int interval = m_clock_speed_hz / m_trigger_rate_hz.load(); + uint64_t interval = m_clock_speed_hz / m_trigger_rate_hz.load(); if( time_distribution == "kUniform"){ return interval; } else if(time_distribution == "kPoisson"){ std::exponential_distribution d(1.0 / interval); - return static_cast(0.5 + d(gen)); + return static_cast(0.5 + d(gen)); } else{ TLOG_DEBUG(1) << get_name() << " unknown distribution! Using kUniform."; diff --git a/plugins/RandomTCMakerModule.hpp b/plugins/RandomTCMakerModule.hpp index c51d1eb..4bc72fc 100644 --- a/plugins/RandomTCMakerModule.hpp +++ b/plugins/RandomTCMakerModule.hpp @@ -116,7 +116,7 @@ class RandomTCMakerModule : public dunedaq::appfwk::DAQModule /// @brief Output trigger rate in hz std::atomic m_trigger_rate_hz{ 0 }; - int get_interval(std::mt19937& gen); + uint64_t get_interval(std::mt19937& gen); dfmessages::run_number_t m_run_number;