• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nasa / trick / 12938103830

23 Jan 2025 09:08PM UTC coverage: 55.937% (+0.05%) from 55.885%
12938103830

Pull #1785

github

web-flow
Merge 8533029a6 into b755b2e7b
Pull Request #1785: Frame Performance Tool- jperf

18 of 21 new or added lines in 1 file covered. (85.71%)

119 existing lines in 10 files now uncovered.

12323 of 22030 relevant lines covered (55.94%)

86209.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.06
/trick_source/sim_services/DataRecord/DataRecordGroup.cpp
1

2
#include <algorithm>
3
#include <string>
4
#include <iostream>
5
#include <sstream>
6
#include <string.h>
7
#include <stdlib.h>
8
#include <iomanip>
9

10
#ifdef __GNUC__
11
#include <cxxabi.h>
12
#endif
13

14
#include "trick/DataRecordGroup.hh"
15
#include "trick/command_line_protos.h"
16
#include "trick/exec_proto.h"
17
#include "trick/reference.h"
18
#include "trick/memorymanager_c_intf.h"
19
#include "trick/message_proto.h"
20
#include "trick/message_type.h"
21

22
/**
23
@details
24
-# The recording group is enabled
25
-# The recording group is set to always record data (not changes only)
26
-# The recording group is set to allocate a maximum of 100,000 records in memory
27
-# The recording group is set to record at a cycle period of 0.1 seconds.
28
-# The first recording parameter is assigned to simulation time.  The name of
29
   the parmeter is named "sys.exec.out.time" to preserve backwards compatibility
30
   with older %Trick releases.
31
-# The call_function and call_function_double functions inherited from the SimObject
32
   are populated.  The data_record function is added as a job for this SimObject.
33

34
A sim object is created named @c data_record_group<n> where <n> is a unique group number. A
35
data_record class job within this sim object is also created that will write the group's
36
recorded data to disk. The job's name is <tt> data_record_group<n>.<in_name></tt>.
37

38
*/
39
Trick::DataRecordBuffer::DataRecordBuffer() {
5,368✔
40
    buffer = last_value = NULL ;
5,368✔
41
    ref = NULL ;
5,368✔
42
    ref_searched = false ;
5,368✔
43
}
5,368✔
44

45
Trick::DataRecordBuffer::~DataRecordBuffer() {
590✔
46
    if ( buffer ) {
590✔
47
        free(buffer) ;
577✔
48
    }
49
    if ( last_value ) {
590✔
50
        free(last_value) ;
578✔
51
    }
52

53
    ref_free(ref) ;
590✔
54
    free(ref) ;
590✔
55
}
590✔
56

57
Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_type ) :
579✔
58
 record(true) ,
59
 inited(false) ,
60
 group_name(in_name) ,
61
 freq(DR_Always),
62
 start(0.0) ,
63
 cycle(0.1) ,
64
 time_value_attr() ,
65
 num_variable_names(0),
66
 variable_names(NULL),
67
 variable_alias(NULL),
68
 num_change_variable_names(0),
69
 change_variable_names(NULL),
70
 change_variable_alias(NULL),
71
 max_num(100000),
72
 buffer_num(0),
73
 writer_num(0),
74
 max_file_size(1<<30), // 1 GB
75
 total_bytes_written(0),
76
 max_size_warning(false),
77
 writer_buff(NULL),
78
 single_prec_only(false),
79
 buffer_type(DR_Buffer),
80
 job_class("data_record"),
81
 curr_time(0.0)
579✔
82
{
83

84
    union {
85
        long l;
86
        char c[sizeof(long)];
87
    } byte_order_union;
88

89
    byte_order_union.l = 1;
579✔
90
    if (byte_order_union.c[sizeof(long) - 1] != 1) {
579✔
91
        byte_order = "little_endian" ;
579✔
92
    } else {
93
        byte_order = "big_endian" ;
×
94
    }
95

96
    // sim object name
97
    name = std::string("trick_data_record_group_") + in_name ;
579✔
98

99
    configure_jobs(dr_type) ;
579✔
100

101
    add_time_variable() ;
579✔
102
}
579✔
103

104
Trick::DataRecordGroup::~DataRecordGroup() {
36✔
105
    free((void *)time_value_attr.units) ;
36✔
106
}
36✔
107

108
int Trick::DataRecordGroup::call_function( Trick::JobData * curr_job ) {
768,832✔
109

110
    int ret = 0 ;
768,832✔
111

112
    switch (curr_job->id) {
768,832✔
113
        case 1:
38✔
114
            ret = init() ;
38✔
115
            break ;
38✔
116
        case 2:
348,530✔
117
            ret = write_data(false) ;
348,530✔
118
            break ;
348,530✔
119
        case 3:
9✔
120
            ret = checkpoint() ;
9✔
121
            break ;
9✔
122
        case 4:
9✔
123
            clear_checkpoint_vars() ;
9✔
124
            break ;
9✔
125
        case 5:
7✔
126
            ret = restart() ;
7✔
127
            break ;
7✔
128
        case 6:
38✔
129
            ret = shutdown() ;
38✔
130
            break ;
38✔
131
        default:
420,201✔
132
            ret = data_record(exec_get_sim_time()) ;
420,201✔
133
            break ;
420,201✔
134
    }
135

136
    return(ret) ;
768,832✔
137

138
}
139

UNCOV
140
double Trick::DataRecordGroup::call_function_double( Trick::JobData * curr_job ) {
×
141
    (void) curr_job ;
UNCOV
142
    return(0.0) ;
×
143
}
144

145
void Trick::DataRecordGroup::register_group_with_mm(void * address , const char * type) {
36✔
146
    // Only add to the memory manager if it has not already been added
147
    if ( TMM_var_exists(name.c_str()) == 0 ) {
36✔
148
        // Declare this to the memory manager.  Must be done here to get the correct type name
149
        TMM_declare_ext_var(address , TRICK_STRUCTURED, type , 0 , name.c_str() , 0 , NULL ) ;
36✔
150
        ALLOC_INFO * alloc_info = get_alloc_info_at(address) ;
36✔
151
        alloc_info->stcl = TRICK_LOCAL ;
36✔
152
        alloc_info->alloc_type = TRICK_ALLOC_NEW ;
36✔
153
    }
154
}
36✔
155

156
const std::string & Trick::DataRecordGroup::get_group_name() {
24✔
157
    return group_name ;
24✔
158
}
159

160
int Trick::DataRecordGroup::set_cycle( double in_cycle ) {
35✔
161
    write_job->set_cycle(in_cycle) ;
35✔
162
    return(0) ;
35✔
163
}
164

165
int Trick::DataRecordGroup::set_phase( unsigned short in_phase ) {
563✔
166
    write_job->phase = in_phase ;
563✔
167
    return(0) ;
563✔
168
}
169

170
int Trick::DataRecordGroup::set_freq( DR_Freq in_freq ) {
31✔
171
    freq = in_freq ;
31✔
172
    return(0) ;
31✔
173
}
174

UNCOV
175
int Trick::DataRecordGroup::set_max_buffer_size( int num ) {
×
UNCOV
176
    max_num = num ;
×
UNCOV
177
    return(0) ;
×
178
}
179

180
int Trick::DataRecordGroup::set_buffer_type( int in_buffer_type ) {
38✔
181
    buffer_type = (DR_Buffering)in_buffer_type ;
38✔
182
    return(0) ;
38✔
183
}
184

185
int Trick::DataRecordGroup::set_max_file_size( uint64_t bytes ) {
14✔
186
    if(bytes == 0) {
14✔
187
        max_file_size = UINT64_MAX ;
×
188
    } else {
189
    max_file_size = bytes ; 
14✔
190
    }
191
    return(0) ;
14✔
192
}
193

194
int Trick::DataRecordGroup::set_single_prec_only( bool in_single_prec_only ) {
19✔
195
    single_prec_only = in_single_prec_only ;
19✔
196
    return(0) ;
19✔
197
}
198

199
int Trick::DataRecordGroup::set_thread( unsigned int in_thread_id ) {
191✔
200

201
    unsigned int jj ;
202
    Trick::JobData * temp_job  ;
203

204
    /* make all data_record_groups have same sim object id as data_record */
205
    for ( jj = 0 ; jj < jobs.size() ; jj++ ) {
1,729✔
206
        temp_job = jobs[jj] ;
1,538✔
207
        temp_job->thread = in_thread_id ;
1,538✔
208
    }
209
    return 0 ;
191✔
210
}
211

212
int Trick::DataRecordGroup::set_job_class( std::string in_class ) {
563✔
213
    write_job->job_class_name = job_class = in_class ;
563✔
214
    return(0) ;
563✔
215
}
216

217
int Trick::DataRecordGroup::add_time_variable() {
613✔
218
    REF2 * new_ref ;
219

220
    // Create attributes for time recorded as a double
221
    time_value_attr.type = TRICK_DOUBLE ;
613✔
222
    time_value_attr.size = sizeof(double) ;
613✔
223
    time_value_attr.units = strdup("s") ;
613✔
224

225
    // Create a reference that records time as sys.exec.out.time
226
    new_ref = (REF2 *)calloc( 1 , sizeof(REF2));
613✔
227
    new_ref->reference = strdup("sys.exec.out.time") ;
613✔
228
    new_ref->address = &curr_time ;
613✔
229
    new_ref->attr = &time_value_attr ;
613✔
230
    add_variable(new_ref) ;
613✔
231

232
    return 0 ;
613✔
233
}
234

235
int Trick::DataRecordGroup::add_variable( std::string in_name , std::string alias ) {
908✔
236

237
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
908✔
238
    // Trim leading spaces
239
    in_name.erase( 0, in_name.find_first_not_of( " \t" ) );
908✔
240
    // Trim trailing spaces
241
    in_name.erase( in_name.find_last_not_of( " \t" ) + 1);
908✔
242
    new_var->name = in_name ;
908✔
243
    new_var->alias = alias ;
908✔
244
    rec_buffer.push_back(new_var) ;
908✔
245
    return 0 ;
908✔
246
}
247

UNCOV
248
void Trick::DataRecordGroup::remove_variable( std::string in_name ) {
×
249
    // Trim leading spaces++ 
UNCOV
250
    in_name.erase( 0, in_name.find_first_not_of( " \t" ) );
×
251
    // Trim trailing spaces
UNCOV
252
    in_name.erase( in_name.find_last_not_of( " \t" ) + 1);
×
253

UNCOV
254
    if (!in_name.compare("sys.exec.out.time")) {
×
255
        // This class assumes sim time is always the first variable.
256
        // Removing it results in errors.
UNCOV
257
        return;
×
258
    }
259

UNCOV
260
    auto remove_from = [&](std::vector<DataRecordBuffer*>& buffer) {
×
261
        for (auto i = buffer.begin(); i != buffer.end(); ++i) {
×
UNCOV
262
            if (!(*i)->name.compare(in_name)) {
×
263
                delete *i;
×
UNCOV
264
                buffer.erase(i);
×
265
                break;
×
266
            }
267
        }
268
    };
×
269

UNCOV
270
    remove_from(rec_buffer);
×
271
    remove_from(change_buffer);
×
272
}
273

274
void Trick::DataRecordGroup::remove_all_variables() {
45✔
275
    // remove all but the first variable, which is sim time
276
    if(!rec_buffer.empty()) {
45✔
277
        for (auto i = rec_buffer.begin() + 1; i != rec_buffer.end(); ++i) {
571✔
278
            delete *i;
526✔
279
        }
280
        rec_buffer.erase(rec_buffer.begin() + 1, rec_buffer.end());
45✔
281
    }
282

283
    // remove everything
284
    for (auto variable : change_buffer) {
51✔
285
        delete variable;
6✔
286
    }
287

288
    change_buffer.clear();
45✔
289
}
45✔
290

291
int Trick::DataRecordGroup::add_variable( REF2 * ref2 ) {
4,454✔
292

293
    if ( ref2 == NULL || ref2->attr == NULL ) {
4,454✔
UNCOV
294
        return(-1) ;
×
295
    }
296

297
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
4,454✔
298
    new_var->name = std::string(ref2->reference) ;
4,454✔
299
    new_var->ref_searched = true ;
4,454✔
300
    new_var->ref = ref2 ;
4,454✔
301
    new_var->last_value = (char *)calloc(1 , new_var->ref->attr->size) ;
4,454✔
302
    // Don't allocate space for the temp storage buffer until "init"
303
    rec_buffer.push_back(new_var) ;
4,454✔
304

305
    return(0) ;
4,454✔
306

307
}
308

309
int Trick::DataRecordGroup::add_change_variable( std::string in_name ) {
6✔
310

311
    REF2 * ref2 ;
312

313
    ref2 = ref_attributes(in_name.c_str()) ;
6✔
314

315
    if ( ref2 == NULL || ref2->attr == NULL ) {
6✔
UNCOV
316
        message_publish(MSG_WARNING, "Could not find Data Record change variable %s.\n", in_name.c_str()) ;
×
UNCOV
317
        return(-1) ;
×
318
    }
319

320
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
6✔
321
    new_var->ref = ref2 ;
6✔
322
    new_var->name = in_name;
6✔
323
    new_var->buffer = (char *)malloc(ref2->attr->size) ;
6✔
324
    new_var->last_value =  NULL ;
6✔
325
    memcpy(new_var->buffer , ref2->address , ref2->attr->size) ;
6✔
326
    change_buffer.push_back(new_var) ;
6✔
327

328
    return(0) ;
6✔
329

330
}
331

332
bool Trick::DataRecordGroup::isSupportedType(REF2 * ref2, std::string& message) {
514✔
333
    if (ref2->attr->type == TRICK_STRING || ref2->attr->type == TRICK_STL || ref2->attr->type == TRICK_STRUCTURED) {
514✔
334
        message = "Cannot Data Record variable " + std::string(ref2->reference) + " of unsupported type " + std::to_string(ref2->attr->type);
2✔
335
        return false;
2✔
336
    }
337
    
338
    // If this is an array and not a single value, don't record it
339
    if (ref2->num_index != ref2->attr->num_index) {
512✔
340
        message = "Cannot Data Record arrayed variable " + std::string(ref2->reference);
4✔
341
        return false;
4✔
342
    }
343

344
    return true;
508✔
345
}
346

347
/**
348
@details
349
-# The simulation output directory is retrieved from the CommandLineArguments
350
-# The log header file is created
351
   -# The endianness of the log file is written to the log header.
352
   -# The names of the parameters contained in the log file are written to the header.
353
-# Memory buffers are allocated to store simulation data
354
-# The DataRecordGroupObject (a derived SimObject) is added to the Scheduler.
355
*/
356
int Trick::DataRecordGroup::init() {
48✔
357

358
    unsigned int jj ;
359
    int ret ;
360

361
    // reset counter here so we can "re-init" our recording
362
    buffer_num = writer_num = total_bytes_written = 0 ;
48✔
363

364
    output_dir = command_line_args_get_output_dir() ;
48✔
365
    /* this is the common part of the record file name, the format specific will add the correct suffix */
366
    file_name = output_dir + "/log_" + group_name ;
48✔
367

368
    pthread_mutex_init(&buffer_mutex, NULL);
48✔
369

370
    // Allocate recording space for time.
371
    rec_buffer[0]->buffer = (char *)calloc(max_num , rec_buffer[0]->ref->attr->size) ;
48✔
372
    rec_buffer[0]->last_value = (char *)calloc(1 , rec_buffer[0]->ref->attr->size) ;
48✔
373

374
    /* Loop through all variables looking up names.  Allocate recording space
375
       according to size of the variable */
376
    for (jj = 1; jj < rec_buffer.size() ; jj++) {
600✔
377
        Trick::DataRecordBuffer * drb = rec_buffer[jj] ;
552✔
378
        if ( drb->ref_searched == false ) {
552✔
379
            REF2 * ref2 ;
380

381
            ref2 = ref_attributes(drb->name.c_str()) ;
514✔
382
            if ( ref2 == NULL || ref2->attr == NULL ) {
514✔
UNCOV
383
                message_publish(MSG_WARNING, "Could not find Data Record variable %s.\n", drb->name.c_str()) ;
×
UNCOV
384
                rec_buffer.erase(rec_buffer.begin() + jj--) ;
×
UNCOV
385
                delete drb ;
×
UNCOV
386
                continue ;
×
387
            } else {
388
                std::string message;
514✔
389
                if (!isSupportedType(ref2, message)) {
514✔
390
                    message_publish(MSG_WARNING, "%s\n", message.c_str()) ;
6✔
391
                    rec_buffer.erase(rec_buffer.begin() + jj--) ;
6✔
392
                    delete drb ;
6✔
393
                    continue ;
6✔
394
                } else {
395
                    drb->ref = ref2 ;
508✔
396
                }
397
            }
398
        }
399
        if ( drb->alias.compare("") ) {
546✔
400
            drb->ref->reference = strdup(drb->alias.c_str()) ;
16✔
401
        }
402
        drb->last_value = (char *)calloc(1 , drb->ref->attr->size) ;
546✔
403
        drb->buffer = (char *)calloc(max_num , drb->ref->attr->size) ;
546✔
404
        drb->ref_searched = true ;
546✔
405
    }
406

407
    write_header() ;
48✔
408

409
    // call format specific initialization to open destination and write header
410
    ret = format_specific_init() ;
48✔
411

412
    // set the inited flag to true when all initialization is done
413
    if ( ret == 0 ) {
48✔
414
        inited = true ;
48✔
415
    }
416

417
    return(0) ;
48✔
418

419
}
420

421
void Trick::DataRecordGroup::configure_jobs(DR_Type type) {
579✔
422
    switch(type) {
579✔
423
    default:
43✔
424
        // run the restart job in phase 60001
425
        add_job(0, 5, (char *)"restart", NULL, 1.0, (char *)"restart", (char *)"TRK", 60001) ;
43✔
426

427
    case DR_Type::DR_Type_FrameLogDataRecord:
579✔
428
        // add_jobs_to_queue will fill in job_id later
429
        // make the init job run after all other initialization jobs but before the post init checkpoint
430
        // job so users can allocate memory in initialization jobs and checkpointing data rec groups will work
431
        add_job(0, 1, (char *)"initialization", NULL, cycle, (char *)"init", (char *)"TRK", 65534) ;
579✔
432
        add_job(0, 2, (char *)"end_of_frame", NULL, 1.0, (char *)"write_data", (char *)"TRK") ;
579✔
433
        add_job(0, 3, (char *)"checkpoint", NULL, 1.0, (char *)"checkpoint", (char *)"TRK") ;
579✔
434
        add_job(0, 4, (char *)"post_checkpoint", NULL, 1.0, (char *)"clear_checkpoint_vars", (char *)"TRK") ;
579✔
435
        add_job(0, 6, (char *)"shutdown", NULL, 1.0, (char *)"shutdown", (char *)"TRK") ;
579✔
436

437
        write_job = add_job(0, 99, (char *)job_class.c_str(), NULL, cycle, (char *)"data_record" , (char *)"TRK") ;
579✔
438
        break ;
579✔
439
    }
440
}
579✔
441

442
int Trick::DataRecordGroup::checkpoint() {
9✔
443
    unsigned int jj ;
444

445
    /*
446
       Save the names of the variables and the aliases to the checkpoint,
447
       the rest of the DataRecordBuffer will be reconstructed during restart.
448
       The first variable is time which we do not have to save.
449
     */
450
    if ( rec_buffer.size() > 1 ) {
9✔
451
        num_variable_names = rec_buffer.size() - 1 ;
9✔
452
        variable_names = (char **)TMM_declare_var_1d("char *", (int)rec_buffer.size() - 1) ;
9✔
453
        variable_alias = (char **)TMM_declare_var_1d("char *", (int)rec_buffer.size() - 1) ;
9✔
454

455
        for (jj = 1; jj < rec_buffer.size() ; jj++) {
38✔
456
            Trick::DataRecordBuffer * drb = rec_buffer[jj] ;
29✔
457

458
            variable_names[jj-1] = TMM_strdup((char *)drb->name.c_str()) ;
29✔
459
            variable_alias[jj-1] = TMM_strdup((char *)drb->alias.c_str()) ;
29✔
460
        }
461
    }
462

463
    /*
464
       Save the names of the change variables and the aliases to the checkpoint,
465
       the rest of the DataRecordBuffer will be reconstructed during restart
466
     */
467
    if ( change_buffer.size() > 0 ) {
9✔
468
        num_change_variable_names = change_buffer.size() ;
2✔
469
        change_variable_names = (char **)TMM_declare_var_1d("char *", (int)change_buffer.size()) ;
2✔
470
        change_variable_alias = (char **)TMM_declare_var_1d("char *", (int)change_buffer.size()) ;
2✔
471

472
        for (jj = 0; jj < change_buffer.size() ; jj++) {
4✔
473
            Trick::DataRecordBuffer * drb = change_buffer[jj] ;
2✔
474

475
            change_variable_names[jj] = TMM_strdup((char *)drb->name.c_str()) ;
2✔
476
            change_variable_alias[jj] = TMM_strdup((char *)drb->alias.c_str()) ;
2✔
477
        }
478
    }
479

480
    return 0 ;
9✔
481
}
482

483
void Trick::DataRecordGroup::clear_checkpoint_vars() {
43✔
484
    
485
    if ( variable_names ) {
43✔
486
        for(unsigned int jj = 0; jj < num_variable_names; jj++) {
58✔
487
            TMM_delete_var_a(variable_names[jj]);
42✔
488
        }
489
        TMM_delete_var_a(variable_names) ;
16✔
490
    }
491

492
    if ( variable_alias ) {
43✔
493
        for(unsigned int jj = 0; jj < num_variable_names; jj++) {
58✔
494
            TMM_delete_var_a(variable_alias[jj]);
42✔
495
        }
496
        TMM_delete_var_a(variable_alias) ;
16✔
497
    }
498

499
    if ( change_variable_names ) {
43✔
500
        for(unsigned int jj = 0; jj < num_change_variable_names; jj++) {
8✔
501
            TMM_delete_var_a(change_variable_names[jj]);
4✔
502
        }
503
        TMM_delete_var_a(change_variable_names) ;
4✔
504
    }
505

506
    if ( change_variable_alias ) {
43✔
507
        for(unsigned int jj = 0; jj < num_change_variable_names; jj++) {
8✔
508
            TMM_delete_var_a(change_variable_alias[jj]);
4✔
509
        }
510
        TMM_delete_var_a(change_variable_alias) ;
4✔
511
    }
512

513
    variable_names = NULL ;
43✔
514
    variable_alias = NULL ;
43✔
515
    change_variable_names = NULL ;
43✔
516
    change_variable_alias = NULL ;
43✔
517
    num_variable_names = 0 ;
43✔
518
    num_change_variable_names = 0 ;
43✔
519
}
43✔
520

521
int Trick::DataRecordGroup::restart() {
7✔
522
    std::vector <Trick::DataRecordBuffer *>::iterator drb_it ;
7✔
523

524
    /* delete the current rec_buffer */
525
    for ( drb_it = rec_buffer.begin() ; drb_it != rec_buffer.end() ; ++drb_it ) {
14✔
526
        delete *drb_it ;
7✔
527
    }
528
    rec_buffer.clear() ;
7✔
529
    /* Add back the time variable */
530
    add_time_variable() ;
7✔
531

532
    /* delete the current change_buffer contents */
533
    for ( drb_it = change_buffer.begin() ; drb_it != change_buffer.end() ; ++drb_it ) {
7✔
UNCOV
534
        delete *drb_it ;
×
535
    }
536
    change_buffer.clear() ;
7✔
537

538
    unsigned int jj ;
539
    /* add the variable names listed in the checkpoint file */
540
    for ( jj = 0 ; jj < num_variable_names ; jj++ ) {
20✔
541
        add_variable( variable_names[jj] , variable_alias[jj] ) ;
13✔
542
    }
543
    for ( jj = 0 ; jj < num_change_variable_names ; jj++ ) {
9✔
544
        add_change_variable( change_variable_names[jj] ) ;
2✔
545
    }
546

547
    clear_checkpoint_vars() ;
7✔
548

549
    // set the write job class to what is in the checkpoint file.
550
    write_job->job_class_name = job_class ;
7✔
551

552
    // reset the sim_object name.
553
    name = std::string("data_record_group_") + group_name ;
7✔
554

555
    /* call init to open the recording file and look up variable name addresses */
556
    init() ;
7✔
557

558
    return 0 ;
7✔
559
}
560

561
int Trick::DataRecordGroup::write_header() {
48✔
562

563
    unsigned int jj ;
564
    std::string header_name ;
96✔
565
    std::fstream out_stream ;
96✔
566

567
    /*! create the header file used by the GUIs */
568
    header_name = output_dir + "/log_" + group_name + ".header" ;
48✔
569

570
    out_stream.open(header_name.c_str(), std::fstream::out ) ;
48✔
571
    if ( ! out_stream  ||  ! out_stream.good() ) {
48✔
UNCOV
572
        return -1;
×
573
    }
574

575
    /* Header file first line is created in format specific header */
576
    out_stream << "log_" << group_name ;
48✔
577

578
    format_specific_header(out_stream) ;
48✔
579

580
    /* Output the file name, variable size, units, and variable name
581
     * to the rest of recorded data header file.
582
     * (e.g. file_name  C_type  units  sim_name)
583
     * Note: "sys.exec.out.time" should be the first variable in the buffer.
584
     */
585
    for (jj = 0; jj < rec_buffer.size() ; jj++) {
642✔
586
        /*! recording single data item */
587
        out_stream << "log_" << group_name << "\t"
594✔
588
            << type_string(rec_buffer[jj]->ref->attr->type,
1,188✔
589
                           rec_buffer[jj]->ref->attr->size) << "\t"
594✔
590
            << std::setw(6) ;
1,188✔
591

592
        if ( rec_buffer[jj]->ref->attr->mods & TRICK_MODS_UNITSDASHDASH ) {
594✔
593
            out_stream << "--" ;
4✔
594
        } else {
595
            out_stream << rec_buffer[jj]->ref->attr->units ;
590✔
596
        }
597
        out_stream << "\t" << rec_buffer[jj]->ref->reference << std::endl ;
594✔
598
    }
599

600
    // Send all unwritten characters in the buffer to its output/file.
601
    out_stream.flush() ;
48✔
602
    out_stream.close() ;
48✔
603

604
    return(0) ;
48✔
605

606
}
607

608
int Trick::DataRecordGroup::data_record(double in_time) {
420,201✔
609

610
    unsigned int jj ;
611
    unsigned int buffer_offset ;
612
    Trick::DataRecordBuffer * drb ;
613
    bool change_detected = false ;
420,201✔
614

615
    //TODO: does not handle bitfields correctly!
616
    if ( record == true ) {
420,201✔
617
        if ( freq != DR_Always ) {
420,201✔
618
            for (jj = 0; jj < change_buffer.size() ; jj++) {
1,644✔
619
                drb = change_buffer[jj] ;
822✔
620
                REF2 * ref = drb->ref ;
822✔
621
                if ( ref->pointer_present == 1 ) {
822✔
UNCOV
622
                    ref->address = follow_address_path(ref) ;
×
623
                }
624
                if ( memcmp( drb->buffer , drb->ref->address , drb->ref->attr->size) ) {
822✔
625
                    change_detected = true ;
28✔
626
                    memcpy( drb->buffer , drb->ref->address , drb->ref->attr->size) ;
28✔
627
                }
628
            }
629

630
        }
631

632
        if ( freq == DR_Always || change_detected == true ) {
420,201✔
633

634
            // If this is not the ring buffer and
635
            // we are going to have trouble fitting 2 data sets then write the data now.
636
            if ( buffer_type != DR_Ring_Buffer ) {
419,407✔
637
                if ( buffer_num - writer_num >= (max_num - 2) ) {
160,207✔
638
                    write_data(true) ;
×
639
                }
640
            }
641

642
            curr_time = in_time ;
419,407✔
643

644
            if ( freq == DR_Changes_Step ) {
419,407✔
UNCOV
645
                buffer_offset = buffer_num % max_num ;
×
646
                *((double *)(rec_buffer[0]->last_value)) = in_time ;
×
647
                for (jj = 0; jj < rec_buffer.size() ; jj++) {
×
648
                    drb = rec_buffer[jj] ;
×
649
                    REF2 * ref = drb->ref ;
×
650
                    int param_size = ref->attr->size ;
×
651
                    if ( buffer_offset == 0 ) {
×
652
                       drb->curr_buffer = drb->buffer ;
×
653
                    } else {
654
                       drb->curr_buffer += param_size ;
×
655
                    }
656
                    switch ( param_size ) {
×
657
                        case 8:
×
658
                            *(int64_t *)drb->curr_buffer = *(int64_t *)drb->last_value ;
×
659
                            break ;
×
660
                        case 4:
×
661
                            *(int32_t *)drb->curr_buffer = *(int32_t *)drb->last_value ;
×
UNCOV
662
                            break ;
×
UNCOV
663
                        case 2:
×
664
                            *(int16_t *)drb->curr_buffer = *(int16_t *)drb->last_value ;
×
UNCOV
665
                            break ;
×
UNCOV
666
                        case 1:
×
UNCOV
667
                            *(int8_t *)drb->curr_buffer = *(int8_t *)drb->last_value ;
×
UNCOV
668
                            break ;
×
UNCOV
669
                        default:
×
UNCOV
670
                            memcpy( drb->curr_buffer , drb->last_value , param_size ) ;
×
UNCOV
671
                            break ;
×
672
                    }
673
                }
UNCOV
674
                buffer_num++ ;
×
675
            }
676

677
            buffer_offset = buffer_num % max_num ;
419,407✔
678
            for (jj = 0; jj < rec_buffer.size() ; jj++) {
4,976,402✔
679
                drb = rec_buffer[jj] ;
4,556,995✔
680
                REF2 * ref = drb->ref ;
4,556,995✔
681
                if ( ref->pointer_present == 1 ) {
4,556,995✔
UNCOV
682
                    ref->address = follow_address_path(ref) ;
×
683
                }
684
                int param_size = ref->attr->size ;
4,556,995✔
685
                if ( buffer_offset == 0 ) {
4,556,995✔
686
                   drb->curr_buffer = drb->buffer ;
571✔
687
                } else {
688
                   drb->curr_buffer += param_size ;
4,556,424✔
689
                }
690
                switch ( param_size ) {
4,556,995✔
691
                    case 8:
3,602,286✔
692
                        *(int64_t *)drb->curr_buffer = *(int64_t *)ref->address ;
3,602,286✔
693
                        break ;
3,602,286✔
694
                    case 4:
953,989✔
695
                        *(int32_t *)drb->curr_buffer = *(int32_t *)ref->address ;
953,989✔
696
                        break ;
953,989✔
697
                    case 2:
650✔
698
                        *(int16_t *)drb->curr_buffer = *(int16_t *)ref->address ;
650✔
699
                        break ;
650✔
700
                    case 1:
70✔
701
                        *(int8_t *)drb->curr_buffer = *(int8_t *)ref->address ;
70✔
702
                        break ;
70✔
UNCOV
703
                    default:
×
UNCOV
704
                        memcpy( drb->curr_buffer , ref->address , param_size ) ;
×
UNCOV
705
                        break ;
×
706
                }
707
            }
708
            buffer_num++ ;
419,407✔
709
        }
710
    }
711

712
    return(0) ;
420,201✔
713

714
}
715

716
int Trick::DataRecordGroup::write_data(bool must_write) {
390,149✔
717

718
    unsigned int local_buffer_num ;
719
    unsigned int num_to_write ;
720
    unsigned int writer_offset ;
721

722
    if ( record and inited and (buffer_type == DR_No_Buffer or must_write) and (total_bytes_written <= max_file_size)) {
390,149✔
723

724
        // buffer_mutex is used in this one place to prevent forced calls of write_data
725
        // to not overwrite data being written by the asynchronous thread.
726
        pthread_mutex_lock(&buffer_mutex) ;
42,151✔
727

728
        local_buffer_num = buffer_num ;
42,151✔
729
        if ( (local_buffer_num - writer_num) > max_num ) {
42,151✔
UNCOV
730
            num_to_write = max_num ;
×
731
        } else {
732
            num_to_write = (local_buffer_num - writer_num) ;
42,151✔
733
        }
734
        writer_num = local_buffer_num - num_to_write ;
42,151✔
735

736
        //! This loop pulls a "row" of time homogeneous data and writes it to the file
737
        while ( writer_num != local_buffer_num ) {
461,558✔
738

739
            writer_offset = writer_num % max_num ;
419,407✔
740
            //! keep record of bytes written to file. Default max is 1GB
741
            total_bytes_written += format_specific_write_data(writer_offset) ;
419,407✔
742
            writer_num++ ;
419,407✔
743

744
        }
745

746
        if(!max_size_warning && (total_bytes_written > max_file_size)) {
42,151✔
UNCOV
747
            std::cerr << "WARNING: Data record max file size " << (static_cast<double>(max_file_size))/(1<<20) << "MB reached.\n"
×
UNCOV
748
            "https://nasa.github.io/trick/documentation/simulation_capabilities/Data-Record#changing-the-max-file-size-of-a-data-record-group-ascii-and-binary-only" 
×
UNCOV
749
            << std::endl;
×
UNCOV
750
            max_size_warning = true;
×
751
        }
752

753
        pthread_mutex_unlock(&buffer_mutex) ;
42,151✔
754

755
    }
756

757
    return 0 ;
390,099✔
758
}
759

760
int Trick::DataRecordGroup::enable() {
31✔
761
    record = true ;
31✔
762
    return(0) ;
31✔
763
}
764

UNCOV
765
int Trick::DataRecordGroup::disable() {
×
UNCOV
766
    record = false ;
×
UNCOV
767
    return(0) ;
×
768
}
769

770
int Trick::DataRecordGroup::shutdown() {
45✔
771

772
    // Force write out all data
773
    record = true ; // If user disabled group, make sure any recorded data gets written out
45✔
774
    write_data(true) ;
45✔
775
    format_specific_shutdown() ;
45✔
776

777
    remove_all_variables();
45✔
778

779
    // remove_all_variables does not remove sim time
780
    if(!rec_buffer.empty()){
45✔
781
        delete rec_buffer[0];
45✔
782
        rec_buffer.clear();
45✔
783
    }
784

785
    if ( writer_buff ) {
45✔
786
        free(writer_buff) ;
45✔
787
        writer_buff = NULL ;
45✔
788
    }
789

790
    return 0 ;
45✔
791
}
792

793
std::string Trick::DataRecordGroup::type_string( int item_type, int item_size ) {
594✔
794
    switch (item_type) {
594✔
795
        case TRICK_CHARACTER:
2✔
796
                return "char";
2✔
797
                break;
798
        case TRICK_UNSIGNED_CHARACTER:
2✔
799
                return "unsigned_char";
2✔
800
                break;
UNCOV
801
        case TRICK_STRING:
×
UNCOV
802
                return "string";
×
803
                break;
804
        case TRICK_SHORT:
5✔
805
                return "short";
5✔
806
                break;
807
        case TRICK_UNSIGNED_SHORT:
5✔
808
                return "unsigned_short";
5✔
809
                break;
810
        case TRICK_ENUMERATED:
69✔
811
        case TRICK_INTEGER:
812
                return "int";
69✔
813
                break;
814
        case TRICK_UNSIGNED_INTEGER:
5✔
815
                return "unsigned_int";
5✔
816
                break;
817
        case TRICK_LONG:
5✔
818
                return "long";
5✔
819
                break;
820
        case TRICK_UNSIGNED_LONG:
5✔
821
                return "unsigned_long";
5✔
822
                break;
823
        case TRICK_FLOAT:
5✔
824
                return "float";
5✔
825
                break;
826
        case TRICK_DOUBLE:
424✔
827
                if ( single_prec_only ) {
424✔
828
                        return "float";
×
829
                }
830
                else {
831
                        return "double";
424✔
832
                }
833
                break;
834
        case TRICK_BITFIELD:
28✔
835
                if (item_size == sizeof(int)) {
28✔
836
                        return "int";
28✔
837
                } else if (item_size == sizeof(short)) {
×
UNCOV
838
                        return "short";
×
839
                } else {
UNCOV
840
                        return "char";
×
841
                }
842
                break;
843
        case TRICK_UNSIGNED_BITFIELD:
28✔
844
                if (item_size == sizeof(int)) {
28✔
845
                        return "unsigned_int";
28✔
UNCOV
846
                } else if (item_size == sizeof(short)) {
×
UNCOV
847
                        return "unsigned_short";
×
848
                } else {
UNCOV
849
                        return "unsigned_char";
×
850
                }
851
                break;
852
        case TRICK_LONG_LONG:
5✔
853
                return "long_long";
5✔
854
                break;
855
        case TRICK_UNSIGNED_LONG_LONG:
2✔
856
                return "unsigned_long_long";
2✔
857
                break;
858
        case TRICK_BOOLEAN:
4✔
859
#if ( __sun | __APPLE__ )
860
                return "int";
861
#else
862
                return "unsigned_char";
4✔
863
#endif
864
                break;
865
    }
UNCOV
866
    return "";
×
867
}
868

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc