diff --git a/include/trick/DataRecordGroup.hh b/include/trick/DataRecordGroup.hh index 60df9c324..3f879f5a9 100644 --- a/include/trick/DataRecordGroup.hh +++ b/include/trick/DataRecordGroup.hh @@ -19,6 +19,8 @@ PROGRAMMERS: namespace Trick { + class DataRecordGroup; + /** * The DR_Freq enumeration represents the possible Trick data recording frequency. */ @@ -70,12 +72,27 @@ namespace Trick { LoggingCycle(double rate_in); LoggingCycle() = default; void set_rate(double rate_in); + static long long calc_next_tics_on_or_after_input_tic(long long input_tic, long long cycle_tic); double rate_in_seconds{}; /* (s) Logging rate in seconds */ long long rate_in_tics{}; /* (--) Logging rate in sim tics */ long long next_cycle_in_tics{}; /* (--) Next cycle in tics for logging */ }; - class DataRecordGroup : public Trick::SimObject { + class DataRecordGroupJobData : public JobData + { + public: + DataRecordGroupJobData(DataRecordGroup & owner_in); + DataRecordGroupJobData(DataRecordGroup & owner_in, int in_thread, int in_id, std::string in_job_class_name , void* in_sup_class_data, + double in_cycle, std::string in_name, std::string in_tag = "", int in_phase = 60000 , + double in_start = 0.0 , double in_stop = 1.0e37); + + int set_cycle(double rate) override; + void enable() override; + + DataRecordGroup & owner; + }; + + class DataRecordGroup : public SimObject { public: @@ -100,9 +117,6 @@ namespace Trick { /** Start time for data recording.\n */ double start; /**< trick_io(*io) trick_units(s) */ - /** Cycle time for data recording.\n */ - double cycle; /**< trick_io(*io) trick_units(s) */ - /* Fake attributes to use for data recording.\n */ ATTRIBUTES time_value_attr; /**< trick_io(**) */ @@ -192,6 +206,18 @@ namespace Trick { */ int set_job_class(std::string in_class) ; + /** + * Get the total number of rates for this DataRecordGroup instance + * @return total number of rates + */ + size_t get_num_rates(); + + /** + * Get the logging rate according to rate index + * @return cycle in seconds or -1.0 if error + */ + double get_rate(const size_t rateIdx = 0); + /** @brief @userdesc Command to set the rate at which the group's data is recorded (default is 0.1). @par Python Usage: @@ -206,10 +232,19 @@ namespace Trick { @par Python Usage: @code .add_cycle() @endcode @param in_cycle - the recording rate in seconds - @return always 0 + @return vector index of the added rate */ int add_cycle(double in_cycle) ; + /** + * Updates a logging rate by index and cycle. Calling with 0 + * index is equivalent to set_cycle + * @param rate_idx index of the added rate + * @param rate_in New integration rate in seconds + * @return Zero = success, non-zero = failure (rateIdx is invalid). + */ + int set_rate(const size_t rate_idx, const double rate_in); + /** @brief @userdesc Command to set the phase where the group's data is record (default is 60000). @par Python Usage: @@ -450,14 +485,32 @@ namespace Trick { /** Data thread condition mutex. */ pthread_mutex_t buffer_mutex; /**< trick_io(**) */ - /** Current time saved in Trick::DataRecordGroup::data_record.\n */ + /** Current time saved in Trick::DataRecordGroup::data_record when a buffer is written \n */ double curr_time ; /**< trick_io(*i) trick_units(--) */ + /** Current time saved in Trick::DataRecordGroup::data_record the job was recorded. \n */ + double curr_time_dr_job ; /**< trick_io(*i) trick_units(--) */ + /** * Vector of logging rate objects. */ std::vector logging_rates; + /** + * Loop through the required logging rates and check if the rates are valid for the time tic value + * @return true if valid, false if an error is detected + */ + bool check_if_rates_are_valid(); + + /** + * Check if a rate is valid for logging. An invalid rate is detected by + * - being smaller than the time tic value (or 0) + * - not being a whole number when converted to tics * + * @param test_rate Test rate in seconds + * @return 0 if valid, 1 if too small, 2 if it cannot be exactly scheduled + */ + int check_if_rate_is_valid(double test_rate); + /** * Loop through the required logging rates and calculate the * next logging time in tics. @@ -465,11 +518,15 @@ namespace Trick { */ long long calculate_next_logging_tic(long long min_tic); + void emit_rate_error(int rate_err_code, size_t log_idx, double err_rate); + /** * Loop through the required logging rates and advance the next cycle tics of matching rates * @param curr_tic_in - time in tics to match and advance the next cycle tic */ void advance_log_tics_given_curr_tic(long long curr_tic_in); + + static const double default_cyle; } ; } ; diff --git a/include/trick/JobData.hh b/include/trick/JobData.hh index 3c5fde622..9aace6259 100644 --- a/include/trick/JobData.hh +++ b/include/trick/JobData.hh @@ -172,12 +172,12 @@ namespace Trick { /** * Enable the job. */ - void enable() ; + virtual void enable() ; /** * Disable the job. */ - void disable() ; + virtual void disable() ; /** * Sets the job is handled flag diff --git a/include/trick/MultiDtIntegLoopScheduler.hh b/include/trick/MultiDtIntegLoopScheduler.hh index ab2506270..ab58d1b2e 100644 --- a/include/trick/MultiDtIntegLoopScheduler.hh +++ b/include/trick/MultiDtIntegLoopScheduler.hh @@ -111,9 +111,9 @@ public: /** * Updates an integration rate by index and cycle. Calling with 0 - * index is equivalent to set_integ_cycle - * @param rateIdx New integration rate in seconds - * @param integRateIn index of the added rate + * index is equivalent to set_integ_cycle * + * @param rateIdx index of the added rate + * @param integRateIn New integration rate in seconds * @return Zero = success, non-zero = failure (rateIdx is invalid). */ virtual int set_integ_rate(const size_t rateIdx, const double integRateIn); diff --git a/test/SIM_checkpoint_data_recording/README.md b/test/SIM_checkpoint_data_recording/README.md index 6979312a1..add5b050e 100644 --- a/test/SIM_checkpoint_data_recording/README.md +++ b/test/SIM_checkpoint_data_recording/README.md @@ -54,12 +54,18 @@ RUN started with different logging setup Checkpoint loaded at t=5 Expected: logging with multiple rates to start from t=7.01+ with no offset +RUN_test10 +Configured the increment job to execute at 0.1 +A runtime invalid set_cycle of 0 is attempted and a set_cycle of 1/128 which the time_tic_value cannot support are both rejected and logging happens as if neither commands are attempted +Checkpoint dumped with at t=7.01 with multiple loggings rates and multiple calls to set the cycle of both rates throughout the RUN +RUN started with different logging setup +Checkpoint loaded at t=5 +Expected: the varying logging rates specified in add_reads to be processed starting from t=7.01+ with no offset. + RUN_test11 and RUN_test11_redirect RUN_test11 runs with data recording and drops a checkpoint at 5.5 seconds RUN_test11_redirect loads the checkpoint from RUN_test11 at time = 0.0 and redirects the output to its own directory Expected: ref_log_foo and log_foo in RUN_test11 should match, showing that the checkpoint did not overwrite previous data; ref_log_foo and log_foo in RUN_test11_redirect should match, showing that the checkpoint redirected output to the new dir. - - Overall: expectation is that what loads in from the checkpoint should take precedence and overwrite the file of the same name - expect for RUN_test11*. diff --git a/test/SIM_checkpoint_data_recording/RUN_test10/dump.py b/test/SIM_checkpoint_data_recording/RUN_test10/dump.py new file mode 100644 index 000000000..1452d7069 --- /dev/null +++ b/test/SIM_checkpoint_data_recording/RUN_test10/dump.py @@ -0,0 +1,48 @@ +import trick +from trick.unit_test import * + +# This was just here for convenience to dump the checkpoints. + +def main(): + exec(open("Modified_data/foo2.dr").read()) + + dr_group = drg[DR_GROUP_ID] + + dr_group.set_cycle(0.1) + dr_group.add_cycle(100.0) + + trick.checkpoint(7.01) + + trick.exec_set_job_cycle("testSimObject.my_foo.increment", 0, 0.1) + + ss_end = 1800.0 + cs_end = 3600.0 + cs_logging_break = 2879.66 + second_cycle_update = ((cs_end-1.1)+(ss_end+2.4))/2 + ph_logging_break = 1796.6 + stop_time = cs_end+2.6+10.0 + + print("time 0, cycle = 0.1") + print(f'time {2.4}, cycle = {ph_logging_break}') + print(f'time {ss_end-1.1}, cycle = {0.1}') + print(f'time {ss_end+2.4}, cycle = {cs_logging_break}') + print(f'time {second_cycle_update}, 2nd cycle = {cs_logging_break/4}') + print(f'time {cs_end-1.1}, cycle = {0.1}') + print(f'time {stop_time-6.9}, disable job') + print(f'time {stop_time-4.5}, enable job') + print(f'stop = {stop_time}') + + trick.add_read(2.4,f'trick.exec_set_job_cycle("trick_data_record_group_{dr_group.get_group_name()}.data_record", 1, {ph_logging_break})') + trick.add_read(ss_end-1.1,f'trick.get_data_record_group("{dr_group.get_group_name()}").set_cycle(0.1)') + trick.add_read(ss_end+2.4,f'trick.get_data_record_group("{dr_group.get_group_name()}").set_cycle({cs_logging_break})') + trick.add_read(second_cycle_update,f'trick.get_data_record_group("{dr_group.get_group_name()}").set_rate(1, {cs_logging_break/4})') + trick.add_read(cs_end-1.1,f'trick.get_data_record_group("{dr_group.get_group_name()}").set_cycle(0.1)') + trick.add_read(1.0, f'trick.get_data_record_group("{dr_group.get_group_name()}").set_cycle(0.0)') + trick.add_read(2.0, f'trick.get_data_record_group("{dr_group.get_group_name()}").set_rate(1, {1/128})') + trick.add_read(2.0, f'trick.get_data_record_group("{dr_group.get_group_name()}").set_rate(1, {1/128})') + trick.add_read(stop_time-6.9, f'trick.exec_set_job_onoff("trick_data_record_group_{dr_group.get_group_name()}.data_record", 1, 0)') + trick.add_read(stop_time-4.5, f'trick.exec_set_job_onoff("trick_data_record_group_{dr_group.get_group_name()}.data_record", 1, 1)') + trick.stop(stop_time) + +if __name__ == "__main__": + main() diff --git a/test/SIM_checkpoint_data_recording/RUN_test10/ref_log_foo2.csv b/test/SIM_checkpoint_data_recording/RUN_test10/ref_log_foo2.csv new file mode 100644 index 000000000..f49d36eec --- /dev/null +++ b/test/SIM_checkpoint_data_recording/RUN_test10/ref_log_foo2.csv @@ -0,0 +1,179 @@ +sys.exec.out.time {s},testSimObject.my_foo.b {1} + 100,2002 + 200,4002 + 300,6002 + 400,8002 + 500,10002 + 600,12002 + 700,14002 + 800,16002 + 900,18002 + 1000,20002 + 1100,22002 + 1200,24002 + 1300,26002 + 1400,28002 + 1500,30002 + 1600,32002 + 1700,34002 + 1796.6,35934 + 1798.9,35980 + 1799,35982 + 1799.1,35984 + 1799.2,35986 + 1799.3,35988 + 1799.4,35990 + 1799.5,35992 + 1799.6,35994 + 1799.7,35996 + 1799.8,35998 + 1799.9,36000 + 1800,36002 + 1800.1,36004 + 1800.2,36006 + 1800.3,36008 + 1800.4,36010 + 1800.5,36012 + 1800.6,36014 + 1800.7,36016 + 1800.8,36018 + 1800.9,36020 + 1801,36022 + 1801.1,36024 + 1801.2,36026 + 1801.3,36028 + 1801.4,36030 + 1801.5,36032 + 1801.6,36034 + 1801.7,36036 + 1801.8,36038 + 1801.9,36040 + 1802,36042 + 1802.1,36044 + 1802.2,36046 + 1802.3,36048 + 1900,38002 + 2000,40002 + 2100,42002 + 2200,44002 + 2300,46002 + 2400,48002 + 2500,50002 + 2600,52002 + 2700,54002 + 2879.66,57594 + 3598.9,71980 + 3599,71982 + 3599.1,71984 + 3599.2,71986 + 3599.3,71988 + 3599.4,71990 + 3599.5,71992 + 3599.575,71992 + 3599.6,71994 + 3599.7,71996 + 3599.8,71998 + 3599.9,72000 + 3600,72002 + 3600.1,72004 + 3600.2,72006 + 3600.3,72008 + 3600.4,72010 + 3600.5,72012 + 3600.6,72014 + 3600.7,72016 + 3600.8,72018 + 3600.9,72020 + 3601,72022 + 3601.1,72024 + 3601.2,72026 + 3601.3,72028 + 3601.4,72030 + 3601.5,72032 + 3601.6,72034 + 3601.7,72036 + 3601.8,72038 + 3601.9,72040 + 3602,72042 + 3602.1,72044 + 3602.2,72046 + 3602.3,72048 + 3602.4,72050 + 3602.5,72052 + 3602.6,72054 + 3602.7,72056 + 3602.8,72058 + 3602.9,72060 + 3603,72062 + 3603.1,72064 + 3603.2,72066 + 3603.3,72068 + 3603.4,72070 + 3603.5,72072 + 3603.6,72074 + 3603.7,72076 + 3603.8,72078 + 3603.9,72080 + 3604,72082 + 3604.1,72084 + 3604.2,72086 + 3604.3,72088 + 3604.4,72090 + 3604.5,72092 + 3604.6,72094 + 3604.7,72096 + 3604.8,72098 + 3604.9,72100 + 3605,72102 + 3605.1,72104 + 3605.2,72106 + 3605.3,72108 + 3605.4,72110 + 3605.5,72112 + 3605.6,72114 + 3608.1,72164 + 3608.2,72166 + 3608.3,72168 + 3608.4,72170 + 3608.5,72172 + 3608.6,72174 + 3608.7,72176 + 3608.8,72178 + 3608.9,72180 + 3609,72182 + 3609.1,72184 + 3609.2,72186 + 3609.3,72188 + 3609.4,72190 + 3609.5,72192 + 3609.6,72194 + 3609.7,72196 + 3609.8,72198 + 3609.9,72200 + 3610,72202 + 3610.1,72204 + 3610.2,72206 + 3610.3,72208 + 3610.4,72210 + 3610.5,72212 + 3610.6,72214 + 3610.7,72216 + 3610.8,72218 + 3610.9,72220 + 3611,72222 + 3611.1,72224 + 3611.2,72226 + 3611.3,72228 + 3611.4,72230 + 3611.5,72232 + 3611.6,72234 + 3611.7,72236 + 3611.8,72238 + 3611.9,72240 + 3612,72242 + 3612.1,72244 + 3612.2,72246 + 3612.3,72248 + 3612.4,72250 + 3612.5,72252 + 3612.6,72254 diff --git a/test/SIM_checkpoint_data_recording/RUN_test10/unit_test.py b/test/SIM_checkpoint_data_recording/RUN_test10/unit_test.py new file mode 100644 index 000000000..431dba2d2 --- /dev/null +++ b/test/SIM_checkpoint_data_recording/RUN_test10/unit_test.py @@ -0,0 +1,13 @@ +import trick + +def main(): + + exec(open("Modified_data/foo.dr").read()) + + # trick.checkpoint(7.0) + trick.add_read(5.0, 'trick.load_checkpoint("RUN_test10/chkpnt_7.010000")') # contains data recording, starts at t=7.01 + + trick.stop(10.0) + +if __name__ == "__main__": + main() diff --git a/test/SIM_segments/models/SegmentedExecutive/src/SegmentedExecutive.cpp b/test/SIM_segments/models/SegmentedExecutive/src/SegmentedExecutive.cpp index 5f2177050..492f7ab31 100644 --- a/test/SIM_segments/models/SegmentedExecutive/src/SegmentedExecutive.cpp +++ b/test/SIM_segments/models/SegmentedExecutive/src/SegmentedExecutive.cpp @@ -133,7 +133,12 @@ int Trick::SegmentedExecutive::segment_set_jobs_onoff() { job = *it ; // Test to see if the next segment is present in the job tags. // Set the disabled flag to the negation of the tag's presence - job->disabled = !(job->tags.count(next_segment_str)) ; + if(!(job->tags.count(next_segment_str))) { + job->disable() ; + } else + { + job->enable(); + } message_publish(MSG_NORMAL, "segment set job %s to %s\n" , job->name.c_str() , job->disabled ? "disabled" : "enabled" ) ; } diff --git a/test_sims.yml b/test_sims.yml index c6208a12c..e64ced4b8 100644 --- a/test_sims.yml +++ b/test_sims.yml @@ -278,6 +278,9 @@ SIM_checkpoint_data_recording: RUN_test[7-9]/dump.py: phase: -1 returns: 0 + RUN_test10/dump.py: + phase: -1 + returns: 0 RUN_test11/dump.py: phase: -1 returns: 0 @@ -318,6 +321,10 @@ SIM_checkpoint_data_recording: returns: 0 compare: - test/SIM_checkpoint_data_recording/RUN_test9/ref_log_foo2.csv vs. test/SIM_checkpoint_data_recording/RUN_test9/log_foo2.csv + RUN_test10/unit_test.py: + returns: 0 + compare: + - test/SIM_checkpoint_data_recording/RUN_test10/ref_log_foo2.csv vs. test/SIM_checkpoint_data_recording/RUN_test10/log_foo2.csv RUN_test11_redirect/unit_test.py: returns: 0 compare: diff --git a/trick_source/sim_services/DataRecord/DataRecordGroup.cpp b/trick_source/sim_services/DataRecord/DataRecordGroup.cpp index b49b3da6b..04b752f05 100644 --- a/trick_source/sim_services/DataRecord/DataRecordGroup.cpp +++ b/trick_source/sim_services/DataRecord/DataRecordGroup.cpp @@ -20,6 +20,9 @@ #include "trick/message_proto.h" #include "trick/message_type.h" + +const double Trick::DataRecordGroup::default_cyle = 0.1; + /** @details -# The recording group is enabled @@ -55,7 +58,6 @@ Trick::DataRecordBuffer::~DataRecordBuffer() { free(ref) ; } - Trick::LoggingCycle::LoggingCycle(double rate_in) { set_rate(rate_in); @@ -63,16 +65,48 @@ Trick::LoggingCycle::LoggingCycle(double rate_in) void Trick::LoggingCycle::set_rate(double rate_in) { - long long curr_tic = exec_get_time_tics(); rate_in_seconds = rate_in; - long long cycle_tics = (long long)round(rate_in * Trick::JobData::time_tic_value); - rate_in_tics = cycle_tics; - if((curr_tic % cycle_tics) != 0) + rate_in_tics = (long long)round(rate_in * Trick::JobData::time_tic_value); +} + +long long Trick::LoggingCycle::calc_next_tics_on_or_after_input_tic(long long input_tic, long long cycle_tic) +{ + long long next_tic; + if((input_tic % cycle_tic) != 0) { - next_cycle_in_tics = (curr_tic/cycle_tics) * cycle_tics + cycle_tics; + next_tic = (input_tic/cycle_tic) * cycle_tic + cycle_tic; } else { - next_cycle_in_tics = curr_tic; + next_tic = input_tic; + } + return next_tic; +} + +Trick::DataRecordGroupJobData::DataRecordGroupJobData(Trick::DataRecordGroup &owner_in) + : owner(owner_in) +{ +} + +Trick::DataRecordGroupJobData::DataRecordGroupJobData(Trick::DataRecordGroup &owner_in, int in_thread, int in_id, std::string in_job_class_name, void *in_sup_class_data, + double in_cycle, std::string in_name, std::string in_tag, int in_phase, + double in_start, double in_stop) + : Trick::JobData(in_thread, in_id, in_job_class_name, in_sup_class_data, in_cycle, in_name, in_tag, in_phase, in_start, in_stop), + owner(owner_in) +{ +} + +int Trick::DataRecordGroupJobData::set_cycle(double rate) +{ + owner.set_cycle(rate); + return 0; +} + +void Trick::DataRecordGroupJobData::enable() +{ + if(disabled) + { + Trick::JobData::enable(); + owner.set_cycle(owner.get_rate()); } } @@ -81,8 +115,7 @@ Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_ inited(false) , group_name(in_name) , freq(DR_Always), - start(0.0) , - cycle(0.1) , + start(0.0), time_value_attr() , num_variable_names(0), variable_names(NULL), @@ -100,7 +133,8 @@ Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_ single_prec_only(false), buffer_type(DR_Buffer), job_class("data_record"), - curr_time(0.0) + curr_time(0.0), + curr_time_dr_job(0.0) { union { @@ -122,7 +156,7 @@ Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_ add_time_variable() ; - logging_rates.emplace_back(cycle); + logging_rates.emplace_back(default_cyle); } Trick::DataRecordGroup::~DataRecordGroup() { @@ -181,16 +215,68 @@ const std::string & Trick::DataRecordGroup::get_group_name() { return group_name ; } +size_t Trick::DataRecordGroup::get_num_rates(){ + return logging_rates.size(); +} + +double Trick::DataRecordGroup::get_rate(const size_t rateIdx) +{ + if(rateIdx < logging_rates.size()) + { + return logging_rates[rateIdx].rate_in_seconds; + } + return -1.0; +} + int Trick::DataRecordGroup::set_cycle( double in_cycle ) { - logging_rates[0].set_rate(in_cycle); - write_job->set_cycle(in_cycle) ; - return(0) ; + return set_rate(0, in_cycle); } int Trick::DataRecordGroup::add_cycle(double in_cycle) { logging_rates.emplace_back(in_cycle); - return(0); + set_rate(logging_rates.size()-1, in_cycle); + return (int)logging_rates.size()-1; +} + +int Trick::DataRecordGroup::set_rate(const size_t rate_idx, const double rate_in) +{ + if(rate_idx >= logging_rates.size()) + { + message_publish(MSG_ERROR, "DataRecordGroup ERROR: DR Group \"%s\" : set_rate: invalid rate idx %lu\n", group_name.c_str(), rate_idx); + return 1; + } + if(inited) { + int ret = check_if_rate_is_valid(rate_in); + if(ret) + { + emit_rate_error(ret, rate_idx, rate_in); + message_publish(MSG_ERROR, "DataRecordGroup ERROR: DR Group \"%s\" : Rejecting runtime set_rate(%lu, %.16g)\n", group_name.c_str(), rate_idx, rate_in); + return 1; + } + long long prev_log_tics = (long long)round(curr_time_dr_job * Trick::JobData::time_tic_value); + long long curr_time_tic = exec_get_time_tics(); + LoggingCycle & curLog = logging_rates[rate_idx]; + curLog.set_rate(rate_in); + curLog.next_cycle_in_tics = LoggingCycle::calc_next_tics_on_or_after_input_tic(curr_time_tic, curLog.rate_in_tics); + if(curLog.next_cycle_in_tics == curr_time_tic && curLog.next_cycle_in_tics == prev_log_tics) + { + curLog.next_cycle_in_tics += curLog.rate_in_tics; + } + for(auto & logCycle : logging_rates) + { + logCycle.next_cycle_in_tics = 0; + } + advance_log_tics_given_curr_tic(curr_time_tic-1); + write_job->next_tics = calculate_next_logging_tic(curr_time_tic-1); + + long long next_next_tics = calculate_next_logging_tic(write_job->next_tics); + write_job->cycle_tics = next_next_tics - write_job->next_tics; + write_job->cycle = (double)write_job->cycle_tics / Trick::JobData::time_tic_value; + } else { + logging_rates[rate_idx].set_rate(rate_in); + } + return 0; } int Trick::DataRecordGroup::set_phase( unsigned short in_phase ) { @@ -344,7 +430,7 @@ int Trick::DataRecordGroup::add_change_variable( std::string in_name ) { ref2 = ref_attributes(in_name.c_str()) ; if ( ref2 == NULL || ref2->attr == NULL ) { - message_publish(MSG_WARNING, "Could not find Data Record change variable %s.\n", in_name.c_str()) ; + message_publish(MSG_WARNING, "DR Group \"%s\" : Could not find Data Record change variable %s.\n", group_name.c_str(), in_name.c_str()) ; return(-1) ; } @@ -411,14 +497,14 @@ int Trick::DataRecordGroup::init(bool is_restart) { ref2 = ref_attributes(drb->name.c_str()) ; if ( ref2 == NULL || ref2->attr == NULL ) { - message_publish(MSG_WARNING, "Could not find Data Record variable %s.\n", drb->name.c_str()) ; + message_publish(MSG_WARNING, "DR Group \"%s\" : Could not find Data Record variable %s.\n", group_name.c_str(), drb->name.c_str()) ; rec_buffer.erase(rec_buffer.begin() + jj--) ; delete drb ; continue ; } else { std::string message; if (!isSupportedType(ref2, message)) { - message_publish(MSG_WARNING, "%s\n", message.c_str()) ; + message_publish(MSG_WARNING, "DR Group \"%s\" : %s\n", group_name.c_str(), message.c_str()) ; rec_buffer.erase(rec_buffer.begin() + jj--) ; delete drb ; continue ; @@ -442,46 +528,12 @@ int Trick::DataRecordGroup::init(bool is_restart) { if(!is_restart) { - long long curr_tics = exec_get_time_tics(); - int tic_value = exec_get_time_tic_value(); - - for(size_t ii = 0; ii < logging_rates.size(); ++ii) + if(!check_if_rates_are_valid()) { - double logging_rate = logging_rates[ii].rate_in_seconds; - if(logging_rate < (1.0 / tic_value)) - { - message_publish( - MSG_ERROR, - "DataRecordGroup ERROR: Cycle for %lu logging rate idx is less than time tic value. cycle = " - "%16.12f, time_tic = %16.12f\n", - ii, - logging_rate, - tic_value); - ret = -1; - } - long long cycle_tics = (long long)round(logging_rate * Trick::JobData::time_tic_value); - - /* Calculate the if the cycle_tics would be a whole number */ - double test_rem = fmod(logging_rate * (double)tic_value, 1.0); - - if(test_rem > 0.001) - { - message_publish(MSG_WARNING, - "DataRecordGroup ERROR: Cycle for %lu logging rate idx cannot be exactly scheduled " - "with time tic value. " - "cycle = %16.12f, cycle_tics = %lld , time_tic = %16.12f\n", - ii, - logging_rate, - cycle_tics, - 1.0 / tic_value); - ret = -1; - } - - // We've checked that the rate is achievable. Call set_rate again to make sure the latest time_tic_value - // was utilized for calculated next tics - logging_rates[ii].set_rate(logging_rate); + disable(); + return (1); } - + long long curr_tics = exec_get_time_tics(); write_job->next_tics = curr_tics; long long next_next_tics = calculate_next_logging_tic(write_job->next_tics); @@ -508,13 +560,14 @@ void Trick::DataRecordGroup::configure_jobs(DR_Type type) { // add_jobs_to_queue will fill in job_id later // make the init job run after all other initialization jobs but before the post init checkpoint // job so users can allocate memory in initialization jobs and checkpointing data rec groups will work - add_job(0, 1, (char *)"initialization", NULL, cycle, (char *)"init", (char *)"TRK", 65534) ; + add_job(0, 1, (char *)"initialization", NULL, default_cyle, (char *)"init", (char *)"TRK", 65534) ; add_job(0, 2, (char *)"end_of_frame", NULL, 1.0, (char *)"write_data", (char *)"TRK") ; add_job(0, 3, (char *)"checkpoint", NULL, 1.0, (char *)"checkpoint", (char *)"TRK") ; add_job(0, 4, (char *)"post_checkpoint", NULL, 1.0, (char *)"clear_checkpoint_vars", (char *)"TRK") ; add_job(0, 6, (char *)"shutdown", NULL, 1.0, (char *)"shutdown", (char *)"TRK") ; - write_job = add_job(0, 99, (char *)job_class.c_str(), NULL, cycle, (char *)"data_record" , (char *)"TRK") ; + write_job = new Trick::DataRecordGroupJobData(*this, 0, 99, (char *)job_class.c_str(), NULL, default_cyle, (char *)"data_record" , (char *)"TRK") ; + jobs.push_back(write_job) ; break ; } write_job->set_system_job_class(true); @@ -693,6 +746,8 @@ int Trick::DataRecordGroup::data_record(double in_time) { Trick::DataRecordBuffer * drb ; bool change_detected = false ; + curr_time_dr_job = in_time; + //TODO: does not handle bitfields correctly! if ( record == true ) { if ( freq != DR_Always ) { @@ -814,6 +869,74 @@ int Trick::DataRecordGroup::data_record(double in_time) { return(0) ; } +bool Trick::DataRecordGroup::check_if_rates_are_valid() +{ + // long long curr_tics = exec_get_time_tics(); + long long tic_value = Trick::JobData::time_tic_value; + bool areValid = true; + + for(size_t ii = 0; ii < logging_rates.size(); ++ii) + { + double logging_rate = logging_rates[ii].rate_in_seconds; + int ret = check_if_rate_is_valid(logging_rate); + if(ret != 0){ + emit_rate_error(ret, ii, logging_rate); + areValid = false; + } + } + return areValid; +} + +void Trick::DataRecordGroup::emit_rate_error(int rate_err_code, size_t log_idx, double err_rate) +{ + long long tic_value = Trick::JobData::time_tic_value; + if(rate_err_code == 1) { + message_publish( + MSG_ERROR, + "DataRecordGroup ERROR: DR Group \"%s\" : Cycle for %lu logging rate idx is less than time tic value. cycle = " + "%16.12f, time_tic = %16.12f\n", + group_name.c_str(), + log_idx, + err_rate, + tic_value); + } else if(rate_err_code == 2) + { + long long cycle_tics = (long long)round(err_rate * tic_value); + message_publish(MSG_ERROR, + "DataRecordGroup ERROR: DR Group \"%s\" : Cycle for %lu logging rate idx cannot be exactly scheduled " + "with time tic value. " + "cycle = %16.12f, cycle_tics = %lld , time_tic = %16.12f\n", + group_name.c_str(), + log_idx, + err_rate, + cycle_tics, + 1.0 / tic_value); + } +} + + int Trick::DataRecordGroup::check_if_rate_is_valid(double test_rate) + { + long long tic_value = Trick::JobData::time_tic_value; + int ret = 0; + + double logging_rate = test_rate; + if(logging_rate < (1.0 / tic_value)) + { + ret = 1; + } else { + /* Calculate the if the cycle_tics would be a whole number */ + double test_rem = fmod(logging_rate * (double)tic_value, 1.0); + + if(test_rem > 0.001) + { + ret = 2; + } + } + + return ret; + } + + /** * Loop through the required logging rates and calculate the * next logging time in tics. @@ -852,8 +975,8 @@ void Trick::DataRecordGroup::advance_log_tics_given_curr_tic(long long curr_tic_ for(size_t cycleIndex = 0; cycleIndex < logging_rates.size(); ++cycleIndex) { long long & logNextTic = logging_rates[cycleIndex].next_cycle_in_tics; - - while(logNextTic <= curr_tic_in) + logNextTic = LoggingCycle::calc_next_tics_on_or_after_input_tic(curr_tic_in, logging_rates[cycleIndex].rate_in_tics); + if(logNextTic <= curr_tic_in) { logNextTic += logging_rates[cycleIndex].rate_in_tics; } diff --git a/trick_source/sim_services/Executive/Executive_add_jobs_to_queue.cpp b/trick_source/sim_services/Executive/Executive_add_jobs_to_queue.cpp index eadbb8d9a..d66b8f25a 100644 --- a/trick_source/sim_services/Executive/Executive_add_jobs_to_queue.cpp +++ b/trick_source/sim_services/Executive/Executive_add_jobs_to_queue.cpp @@ -96,7 +96,7 @@ int Trick::Executive::add_jobs_to_queue( Trick::SimObject * in_sim_object , bool temp_job->calc_cycle_tics() ; if ( temp_job->start > max_time ) { - temp_job->disabled = true ; + temp_job->disable() ; temp_job->start_tics = TRICK_MAX_LONG_LONG ; } else { temp_job->start_tics = (long long)(temp_job->start * time_tic_value) ; @@ -105,7 +105,7 @@ int Trick::Executive::add_jobs_to_queue( Trick::SimObject * in_sim_object , bool if ( temp_job->stop > max_time ) { temp_job->stop_tics = TRICK_MAX_LONG_LONG ; } else if ( temp_job->stop < temp_job->start ) { - temp_job->disabled = true ; + temp_job->disable() ; } else { temp_job->stop_tics = (long long)(temp_job->stop * time_tic_value) ; } diff --git a/trick_source/sim_services/Executive/Executive_check_all_job_cycle_times.cpp b/trick_source/sim_services/Executive/Executive_check_all_job_cycle_times.cpp index 9a67410af..4bccfaeb9 100644 --- a/trick_source/sim_services/Executive/Executive_check_all_job_cycle_times.cpp +++ b/trick_source/sim_services/Executive/Executive_check_all_job_cycle_times.cpp @@ -24,7 +24,7 @@ int Trick::Executive::check_all_job_cycle_times() { if ( temp_job->cycle < (1.0 / time_tic_value) ) { message_publish(MSG_WARNING,"Cycle for (%s) is less than time tic value. cycle = %16.12f, time_tic = %16.12f\n", temp_job->name.c_str() , temp_job->cycle, 1.0 / time_tic_value ) ; - temp_job->disabled = true ; + temp_job->disable() ; temp_job->cycle_tics = TRICK_MAX_LONG_LONG ; temp_job->next_tics = TRICK_MAX_LONG_LONG ; ret = -1 ; diff --git a/trick_source/sim_services/Executive/Executive_set_job_onoff.cpp b/trick_source/sim_services/Executive/Executive_set_job_onoff.cpp index 07aa5e7db..608183047 100644 --- a/trick_source/sim_services/Executive/Executive_set_job_onoff.cpp +++ b/trick_source/sim_services/Executive/Executive_set_job_onoff.cpp @@ -15,14 +15,22 @@ int Trick::Executive::set_job_onoff(std::string job_name, int instance_num , int if ( job != NULL ) { // set disabled flag accordingly for one job (the given job_name) - job->disabled = !on ; + if(on) { + job->enable(); + } else { + job->disable(); + } } else { // job_name may be a tag name: find all jobs that have the given tag name and hold them in a list range = all_tagged_jobs.equal_range(job_name) ; if (range.first != range.second) { // set disabled flag accordingly for all jobs with this tag - for ( it = range.first; it != range.second ; ++it ) { - it->second->disabled = !on ; + for ( it = range.first; it != range.second ; ++it ) { + if(on) { + it->second->enable(); + } else { + it->second->disable(); + } } } else { message_publish(MSG_WARNING, "Warning: Job %s not found in Executive::set_job_onoff\n" , job_name.c_str()) ; diff --git a/trick_source/sim_services/Executive/Executive_set_simobject_onoff.cpp b/trick_source/sim_services/Executive/Executive_set_simobject_onoff.cpp index 60dd5c155..15d2d3c00 100644 --- a/trick_source/sim_services/Executive/Executive_set_simobject_onoff.cpp +++ b/trick_source/sim_services/Executive/Executive_set_simobject_onoff.cpp @@ -33,10 +33,15 @@ int Trick::Executive::set_sim_object_onoff(std::string sim_object_name, int on) for ( int i = 0 ; i < sim_object->jobs.size() ; i++ ) { std::map::iterator saved_state_it = sim_object->saved_job_states.find(sim_object->jobs[i]); if (saved_state_it != sim_object->saved_job_states.end()) { - sim_object->jobs[i]->disabled = saved_state_it->second; + if(saved_state_it->second) + { + sim_object->jobs[i]->disable(); + } else { + sim_object->jobs[i]->enable(); + } } else { // If job is not in map, turn it on - sim_object->jobs[i]->disabled = false; + sim_object->jobs[i]->enable(); } } sim_object->saved_job_states.clear(); @@ -44,7 +49,7 @@ int Trick::Executive::set_sim_object_onoff(std::string sim_object_name, int on) for ( int i = 0 ; i < sim_object->jobs.size() ; i++ ) { // Save state, turn off sim_object->saved_job_states[sim_object->jobs[i]] = sim_object->jobs[i]->disabled; - sim_object->jobs[i]->disabled = true ; + sim_object->jobs[i]->disable() ; } } return(0) ; diff --git a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp index c822ddf98..0728a9276 100644 --- a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp +++ b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp @@ -220,7 +220,7 @@ int Trick::IPPythonEvent::condition_job(int num, std::string jobname, std::strin } else { if (! job->job_class_name.compare("malfunction")) { // enable it if it's a malf job because they are not in any queue - job->disabled = false; + job->enable(); } condition(num, jobname, comment, NULL, job ); } @@ -374,7 +374,7 @@ int Trick::IPPythonEvent::action_job(int num, std::string jobname, std::string c } else { if (! job->job_class_name.compare("malfunction")) { // enable it if it's a malf job because they are not in any queue - job->disabled = false; + job->enable(); } action(num, jobname, comment, job, 3 ); } @@ -574,9 +574,12 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { } else if (condition_list[ii]->job != NULL) { // if it's a job, get its return value bool save_disabled_state = condition_list[ii]->job->disabled; - condition_list[ii]->job->disabled = false; + condition_list[ii]->job->enable(); return_val = condition_list[ii]->job->call(); - condition_list[ii]->job->disabled = save_disabled_state; + if(save_disabled_state) + { + condition_list[ii]->job->disable(); + } } else { // otherwise use python to evaluate string std::string full_in_string ; @@ -637,16 +640,18 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { case 0 : // python, should not get here break; case 1 : // On - action_list[ii]->job->disabled = false; + action_list[ii]->job->enable(); break; case 2 : // Off - action_list[ii]->job->disabled = true; + action_list[ii]->job->disable(); break; case 3 : // Call bool save_disabled_state = action_list[ii]->job->disabled; - action_list[ii]->job->disabled = false; + action_list[ii]->job->enable(); action_list[ii]->job->call(); - action_list[ii]->job->disabled = save_disabled_state; + if(save_disabled_state) { + action_list[ii]->job->disable(); + } break; } } else {