00001 #include "group_recorder.h"
00002 #include <sstream>
00003
00004 CLASS *group_recorder::oclass = NULL;
00005 CLASS *group_recorder::pclass = NULL;
00006 group_recorder *group_recorder::defaults = NULL;
00007
00008 void new_group_recorder(MODULE *mod){
00009 new group_recorder(mod);
00010 }
00011
00012 group_recorder::group_recorder(MODULE *mod){
00013 if(oclass == NULL)
00014 {
00015 #ifdef _DEBUG
00016 gl_debug("construction group_recorder class");
00017 #endif
00018 oclass = gl_register_class(mod,"group_recorder",sizeof(group_recorder), PC_POSTTOPDOWN);
00019 if(oclass == NULL)
00020 GL_THROW("unable to register object class implemented by %s",__FILE__);
00021
00022 if(gl_publish_variable(oclass,
00023 PT_char256, "file", PADDR(filename), PT_DESCRIPTION, "output file name",
00024 PT_char1024, "group", PADDR(group_def), PT_DESCRIPTION, "group definition string",
00025 PT_double, "interval[s]", PADDR(dInterval), PT_DESCRIPTION, "recordering interval (0 'every iteration', -1 'on change')",
00026 PT_double, "flush_interval[s]", PADDR(dFlush_interval), PT_DESCRIPTION, "file flush interval (0 never, negative on samples)",
00027 PT_bool, "strict", PADDR(strict), PT_DESCRIPTION, "causes the group_recorder to stop the simulation should there be a problem opening or writing with the group_recorder",
00028 PT_bool, "print_units", PADDR(print_units), PT_DESCRIPTION, "flag to append units to each written value, if applicable",
00029 PT_char256, "property", PADDR(property_name), PT_DESCRIPTION, "property to record",
00030 PT_int32, "limit", PADDR(limit), PT_DESCRIPTION, "the maximum number of lines to write to the file",
00031 PT_bool, "format", PADDR(format), PT_DESCRIPTION, "determines whether output timestamp is formatted to be formatted as human-readable (default) or epoch",
00032 PT_enumeration, "complex_part", PADDR(complex_part), PT_DESCRIPTION, "the complex part to record if complex properties are gathered",
00033 PT_KEYWORD, "NONE", NONE,
00034 PT_KEYWORD, "REAL", REAL,
00035 PT_KEYWORD, "IMAG", IMAG,
00036 PT_KEYWORD, "MAG", MAG,
00037 PT_KEYWORD, "ANG_DEG", ANG,
00038 PT_KEYWORD, "ANG_RAD", ANG_RAD,
00039 NULL) < 1){
00040 ;
00041 }
00042
00043 if (gl_publish_function(oclass,"obj_postupdate_fxn",(FUNCTIONADDR)group_recorder_postroutine)==NULL)
00044 GL_THROW("Unable to publish deltamode postupdate function for group_recorder");
00045
00046 defaults = this;
00047 memset(this, 0, sizeof(group_recorder));
00048 }
00049 }
00050
00051 int group_recorder::create(){
00052 memcpy(this, defaults, sizeof(group_recorder));
00053 deltamode_gr = false;
00054 return 1;
00055 }
00056
00057 int group_recorder::init(OBJECT *obj){
00058 OBJECT *gr_obj = 0;
00059 OBJECT *thisobj = OBJECTHDR(this);
00060 int retvalue;
00061
00062
00063 if(0 == group_def[0]){
00064 if(strict){
00065 gl_error("group_recorder::init(): no group defined");
00066
00067
00068
00069 return 0;
00070 } else {
00071 gl_warning("group_recorder::init(): no group defined");
00072 tape_status = TS_ERROR;
00073 return 1;
00074 }
00075 }
00076
00077
00078 if(0 == filename[0]){
00079
00080 if(strict){
00081 gl_error("group_recorder::init(): no filename defined in strict mode");
00082 return 0;
00083 } else {
00084 sprintf(filename, "%256s-%256i.csv", oclass->name, obj->id);
00085 gl_warning("group_recorder::init(): no filename defined, auto-generating '%s'", filename.get_string());
00086
00087
00088
00089
00090 }
00091 }
00092
00093
00094 write_interval = (int64)(dInterval);
00095 if(-1 > write_interval){
00096 gl_error("group_recorder::init(): invalid write_interval of %i, must be -1 or greater", write_interval);
00097
00098
00099
00100 return 0;
00101 }
00102
00103
00104 flush_interval = (int64)dFlush_interval;
00105
00106
00107
00108
00109 items = gl_find_objects(FL_GROUP, group_def.get_string());
00110 if(0 == items){
00111 if(strict){
00112 gl_error("group_recorder::init(): unable to construct a set with group definition");
00113
00114
00115
00116 return 0;
00117 } else {
00118 gl_warning("group_recorder::init(): unable to construct a set with group definition");
00119 tape_status = TS_ERROR;
00120 return 1;
00121 }
00122 }
00123 if(1 > items->hit_count){
00124 if(strict){
00125 gl_error("group_recorder::init(): the defined group returned an empty set");
00126
00127
00128
00129 return 0;
00130 } else {
00131 gl_warning("group_recorder::init(): the defined group returned an empty set");
00132 tape_status = TS_ERROR;
00133 return 1;
00134 }
00135 }
00136
00137
00138 rec_file = fopen(filename.get_string(), "w");
00139 if(0 == rec_file){
00140 if(strict){
00141 gl_error("group_recorder::init(): unable to open file '%s' for writing", filename.get_string());
00142 return 0;
00143 } else {
00144 gl_warning("group_recorder::init(): unable to open file '%s' for writing", filename.get_string());
00145
00146
00147
00148 tape_status = TS_ERROR;
00149 return 1;
00150 }
00151 }
00152
00153
00154 obj_count = 0;
00155 for(gr_obj = gl_find_next(items, 0); gr_obj != 0; gr_obj = gl_find_next(items, gr_obj) ){
00156 prop_ptr = gl_get_property(gr_obj, property_name.get_string());
00157
00158 if(prop_ptr == NULL){
00159 gl_error("group_recorder::init(): unable to find property '%s' in an object of type '%s'", property_name.get_string(), gr_obj->oclass->name);
00160
00161
00162
00163 return 0;
00164 }
00165 ++obj_count;
00166 if(obj_list == 0){
00167 obj_list = new quickobjlist(gr_obj, prop_ptr);
00168 } else {
00169 obj_list->tack(gr_obj, prop_ptr);
00170 }
00171 }
00172
00173
00174 if(!print_units){
00175 quickobjlist *itr = obj_list;
00176 for(; itr != 0; itr = itr->next){
00177 itr->prop.unit = NULL;
00178 }
00179 }
00180
00181 tape_status = TS_OPEN;
00182 if(0 == write_header()){
00183 gl_error("group_recorder::init(): an error occured when writing the file header");
00184
00185
00186
00187 tape_status = TS_ERROR;
00188 return 0;
00189 }
00190
00191
00192 if ( (thisobj->flags)&OF_DELTAMODE )
00193 {
00194
00195 retvalue = delta_add_tape_device(thisobj,GROUPRECORDER);
00196
00197
00198 if (retvalue == 0)
00199 {
00200
00201 return 0;
00202 }
00203
00204
00205 deltamode_gr = true;
00206 }
00207
00208 return 1;
00209 }
00210
00211 TIMESTAMP group_recorder::postsync(TIMESTAMP t0, TIMESTAMP t1){
00212
00213
00214
00215 if(0 == write_interval){
00216 if(0 == read_line()){
00217 gl_error("group_recorder::sync");
00218
00219
00220
00221 return 0;
00222 }
00223 } else if(0 < write_interval){
00224
00225 if(last_write + write_interval <= t1){
00226 interval_write = true;
00227 last_write = t1;
00228 next_write = t1 + write_interval;
00229 }
00230
00231 if (deltamode_gr == true)
00232 {
00233
00234 if ((t0 == t1) && (t1 == next_write))
00235 {
00236
00237 next_write = next_write + TS_SECOND;
00238 }
00239
00240 }
00241
00242
00243 return next_write;
00244 } else {
00245
00246 return TS_NEVER;
00247 }
00248
00249 if(0 == write_interval){
00250 if(0 == write_line(t1,0.0,false) ){
00251 gl_error("group_recorder::sync(): error when writing the values to the file");
00252
00253
00254
00255 return 0;
00256 }
00257 if(flush_interval < 0){
00258 if( ((write_count + 1) % (-flush_interval)) == 0 ){
00259 flush_line();
00260 }
00261 }
00262 }
00263
00264
00265 return TS_NEVER;
00266 }
00267
00268 int group_recorder::commit(TIMESTAMP t1, double t1dbl, bool deltacall){
00269
00270 if((TS_ERROR == tape_status) && strict){
00271 gl_error("group_recorder::commit(): the object has error'ed and is halting the simulation");
00272
00273
00274
00275
00276 return 0;
00277 }
00278
00279
00280 if (deltacall==true)
00281 {
00282 t1 = (TIMESTAMP)t1dbl;
00283 }
00284
00285
00286 if(TS_OPEN != tape_status){
00287 return 1;
00288 }
00289
00290
00291 if(write_interval > 0){
00292 if(((interval_write==true) && (deltacall==false)) || (deltacall==true)){
00293 if(0 == read_line()){
00294 gl_error("group_recorder::commit(): error when reading the values");
00295 return 0;
00296 }
00297 if(0 == write_line(t1,t1dbl,deltacall)){
00298 gl_error("group_recorder::commit(): error when writing the values to the file");
00299 return 0;
00300 }
00301 last_write = t1;
00302 interval_write = false;
00303 }
00304 }
00305
00306
00307
00308
00309 if(-1 == write_interval){
00310 if(0 == read_line()){
00311 if(0 == read_line()){
00312 gl_error("group_recorder::commit(): error when reading the values");
00313 return 0;
00314 }
00315 if(0 != strcmp(line_buffer, prev_line_buffer) ){
00316 if(0 == write_line(t1,t1dbl,deltacall)){
00317 gl_error("group_recorder::commit(): error when writing the values to the file");
00318 return 0;
00319 }
00320 }
00321 }
00322
00323 }
00324
00325
00326 if(flush_interval > 0){
00327 if(last_flush + flush_interval <= t1){
00328 last_flush = t1;
00329 }
00330 } else if(flush_interval < 0){
00331 if( ((write_count + 1) % (-flush_interval)) == 0 ){
00332 flush_line();
00333 }
00334 }
00335
00336
00337 if(limit > 0 && write_count >= limit){
00338
00339 write_footer();
00340 fclose(rec_file);
00341 rec_file = 0;
00342 free(line_buffer);
00343 line_buffer = 0;
00344 line_size = 0;
00345 tape_status = TS_DONE;
00346 }
00347
00348
00349 if((TS_ERROR == tape_status) && strict){
00350 gl_error("group_recorder::commit(): the object has error'ed and is halting the simulation");
00351
00352
00353
00354
00355 return 0;
00356 }
00357
00358 return 1;
00359 }
00360
00361 int group_recorder::isa(char *classname){
00362 return (strcmp(classname, oclass->name) == 0);
00363 }
00364
00368 int group_recorder::write_header(){
00369
00370 time_t now = time(NULL);
00371 quickobjlist *qol = 0;
00372 OBJECT *obj=OBJECTHDR(this);
00373
00374 if(TS_OPEN != tape_status){
00375
00376 return 0;
00377 }
00378 if(0 == rec_file){
00379 gl_error("group_recorder::write_header(): the output file was not opened");
00380
00381
00382
00383
00384 tape_status = TS_ERROR;
00385 return 0;
00386 }
00387
00388
00389 if(0 > fprintf(rec_file,"# file...... %s\n", filename.get_string())){ return 0; }
00390 if(0 > fprintf(rec_file,"# date...... %s", asctime(localtime(&now)))){ return 0; }
00391 #ifdef WIN32
00392 if(0 > fprintf(rec_file,"# user...... %s\n", getenv("USERNAME"))){ return 0; }
00393 if(0 > fprintf(rec_file,"# host...... %s\n", getenv("MACHINENAME"))){ return 0; }
00394 #else
00395 if(0 > fprintf(rec_file,"# user...... %s\n", getenv("USER"))){ return 0; }
00396 if(0 > fprintf(rec_file,"# host...... %s\n", getenv("HOST"))){ return 0; }
00397 #endif
00398 if(0 > fprintf(rec_file,"# group..... %s\n", group_def.get_string())){ return 0; }
00399 if(0 > fprintf(rec_file,"# property.. %s\n", property_name.get_string())){ return 0; }
00400 if(0 > fprintf(rec_file,"# limit..... %d\n", limit)){ return 0; }
00401 if(0 > fprintf(rec_file,"# interval.. %d\n", write_interval)){ return 0; }
00402
00403
00404 if(0 > fprintf(rec_file, "# timestamp")){ return 0; }
00405 for(qol = obj_list; qol != 0; qol = qol->next){
00406 if(0 != qol->obj->name){
00407 if(0 > fprintf(rec_file, ",%s", qol->obj->name)){ return 0; }
00408 } else {
00409 if(0 > fprintf(rec_file, ",%s:%i", qol->obj->oclass->name, qol->obj->id)){ return 0; }
00410 }
00411 }
00412 if(0 > fprintf(rec_file, "\n")){ return 0; }
00413 return 1;
00414 }
00415
00419 int group_recorder::read_line(){
00420 size_t index = 0, offset = 0, unit_len = 0;
00421 quickobjlist *curr = 0;
00422 char *swap_ptr = 0;
00423 char buffer[128];
00424 char objname[128];
00425
00426 if(TS_OPEN != tape_status){
00427
00428 return 0;
00429 }
00430
00431
00432 if(line_size <= 0 || line_buffer == 0){
00433 size_t prop_size;
00434
00435
00436
00437 prop_size = 48;
00438
00439 line_size = (prop_size + 1) * obj_count + 1;
00440 line_buffer = (char *)malloc(line_size);
00441 if(0 == line_buffer){
00442 return 0;
00443 }
00444 memset(line_buffer, 0, line_size);
00445 if(-1 == write_interval){
00446 prev_line_buffer = (char *)malloc(line_size);
00447 if(0 == prev_line_buffer){
00448 gl_error("group_recorder::read_line(): malloc failure");
00449
00450
00451
00452 return 0;
00453 }
00454 memset(prev_line_buffer, 0, line_size);
00455 }
00456 }
00457
00458
00459 if(-1 == write_interval){
00460 swap_ptr = prev_line_buffer;
00461 prev_line_buffer = line_buffer;
00462 line_buffer = swap_ptr;
00463 }
00464 memset(line_buffer, 0, line_size);
00465 for(curr = obj_list; curr != 0; curr = curr->next){
00466
00467 if(curr->prop.ptype == PT_complex && complex_part != NONE){
00468 double part_value = 0.0;
00469 complex *cptr = 0;
00470
00471 cptr = gl_get_complex(curr->obj, &(curr->prop));
00472 if(0 == cptr){
00473 gl_error("group_recorder::read_line(): unable to get complex property '%s' from object '%s'", curr->prop.name, gl_name(curr->obj, objname, 127));
00474
00475
00476
00477 return 0;
00478 }
00479
00480 switch(complex_part){
00481 case NONE:
00482
00483 gl_error("group_recorder::read_line(): inconsistant complex_part states!");
00484 return 0;
00485 case REAL:
00486 part_value = cptr->Re();
00487 break;
00488 case IMAG:
00489 part_value = cptr->Im();
00490 break;
00491 case MAG:
00492 part_value = cptr->Mag();
00493 break;
00494 case ANG:
00495 part_value = cptr->Arg() * 180/PI;
00496 break;
00497 case ANG_RAD:
00498 part_value = cptr->Arg();
00499 break;
00500 }
00501 sprintf(buffer, "%f", part_value);
00502 offset = strlen(buffer);
00503 } else {
00504 offset = gl_get_value(curr->obj, GETADDR(curr->obj, &(curr->prop)), buffer, 127, &(curr->prop));
00505 if(0 == offset){
00506 gl_error("group_recorder::read_line(): unable to get value for '%s' in object '%s'", curr->prop.name, curr->obj->name);
00507
00508
00509
00510 return 0;
00511 }
00512 }
00513
00514 if( (index + offset + 1) > line_size ){
00515 gl_error("group_recorder::read_line(): potential buffer overflow from a too-short automatically sized output value buffer");
00516
00517
00518
00519
00520 return 0;
00521 }
00522
00523
00524 if(0 >= sprintf(line_buffer+index, ",%s", buffer)){return 0;}
00525 index += (offset + 1);
00526 }
00527
00528
00529 return 1;
00530 }
00531
00535 int group_recorder::write_line(TIMESTAMP t1, double t1dbl, bool deltacall){
00536 char time_str[64];
00537 DATETIME dt;
00538
00539 if(TS_OPEN != tape_status){
00540 gl_error("group_recorder::write_line(): trying to write line when the tape is not open");
00541
00542 return 0;
00543 }
00544 if(0 == rec_file){
00545 gl_error("group_recorder::write_line(): no output file open and state is 'open'");
00546
00547
00548
00549
00550 tape_status = TS_ERROR;
00551 return 0;
00552 }
00553
00554
00555 if(line_size <= 0 || line_buffer == 0){
00556 gl_error("group_recorder::write_line(): output buffer not initialized (read_line() not called)");
00557
00558
00559
00560 tape_status = TS_ERROR;
00561 return 0;
00562 }
00563
00564
00565
00566 if (format == false)
00567 {
00568 if (deltacall==false)
00569 {
00570 if(0 == gl_localtime(t1, &dt))
00571 {
00572 gl_error("group_recorder::write_line(): error when converting the sync time");
00573
00574
00575
00576 tape_status = TS_ERROR;
00577 return 0;
00578 }
00579 }
00580 else
00581 {
00582 if(0 == gl_localtime_delta(t1dbl, &dt))
00583 {
00584 gl_error("group_recorder::write_line(): error when converting the sync time");
00585
00586
00587
00588 tape_status = TS_ERROR;
00589 return 0;
00590 }
00591 }
00592
00593 if(0 == gl_strtime(&dt, time_str, sizeof(time_str) ) )
00594 {
00595 gl_error("group_recorder::write_line(): error when writing the sync time as a string");
00596
00597
00598
00599 tape_status = TS_ERROR;
00600 return 0;
00601 }
00602 }
00603 else
00604 {
00605
00606 std::string number;
00607 std::stringstream strstream;
00608 strstream << (long long)t1;
00609 strstream >> number;
00610 strcpy(time_str, number.c_str());
00611 }
00612
00613
00614 if(0 >= fprintf(rec_file, "%s%s\n", time_str, line_buffer)){
00615 gl_error("group_recorder::write_line(): error when writing to the output file");
00616
00617
00618
00619 tape_status = TS_ERROR;
00620 return 0;
00621 }
00622 ++write_count;
00623
00624 return 1;
00625 }
00626
00630 int group_recorder::flush_line(){
00631 if(TS_OPEN != tape_status){
00632 gl_error("group_recorder::flush_line(): tape is not open");
00633
00634 return 0;
00635 }
00636 if(0 == rec_file){
00637 gl_error("group_recorder::flush_line(): output file is not open");
00638
00639
00640
00641
00642 tape_status = TS_ERROR;
00643 return 0;
00644 }
00645 if(0 != fflush(rec_file)){
00646 gl_error("group_recorder::flush_line(): unable to flush output file");
00647
00648
00649
00650 tape_status = TS_ERROR;
00651 return 0;
00652 }
00653 return 1;
00654 }
00655
00659 int group_recorder::write_footer(){
00660 if(TS_OPEN != tape_status){
00661 gl_error("group_recorder::write_footer(): tape is not open");
00662
00663 return 0;
00664 }
00665 if(0 == rec_file){
00666 gl_error("group_recorder::write_footer(): output file is not open");
00667
00668
00669
00670
00671 tape_status = TS_ERROR;
00672 return 0;
00673 }
00674
00675
00676 if(0 >= fprintf(rec_file, "# end of file\n")){ return 0; }
00677
00678 return 1;
00679 }
00680
00682
00683
00684 EXPORT int create_group_recorder(OBJECT **obj, OBJECT *parent){
00685 int rv = 0;
00686 try {
00687 *obj = gl_create_object(group_recorder::oclass);
00688 if(*obj != NULL){
00689 group_recorder *my = OBJECTDATA(*obj, group_recorder);
00690 gl_set_parent(*obj, parent);
00691 rv = my->create();
00692 }
00693 }
00694 catch (char *msg){
00695 gl_error("create_group_recorder: %s", msg);
00696 }
00697 catch (const char *msg){
00698 gl_error("create_group_recorder: %s", msg);
00699 }
00700 catch (...){
00701 gl_error("create_group_recorder: unexpected exception caught");
00702 }
00703 return rv;
00704 }
00705
00706 EXPORT int init_group_recorder(OBJECT *obj){
00707 group_recorder *my = OBJECTDATA(obj, group_recorder);
00708 int rv = 0;
00709 try {
00710 rv = my->init(obj->parent);
00711 }
00712 catch (char *msg){
00713 gl_error("init_group_recorder: %s", msg);
00714 }
00715 catch (const char *msg){
00716 gl_error("init_group_recorder: %s", msg);
00717 }
00718 return rv;
00719 }
00720
00721 EXPORT TIMESTAMP sync_group_recorder(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass){
00722 group_recorder *my = OBJECTDATA(obj, group_recorder);
00723 TIMESTAMP rv = 0;
00724 try {
00725 switch(pass){
00726 case PC_PRETOPDOWN:
00727 rv = TS_NEVER;
00728 break;
00729 case PC_BOTTOMUP:
00730 rv = TS_NEVER;
00731 break;
00732 case PC_POSTTOPDOWN:
00733 rv = my->postsync(obj->clock, t0);
00734 obj->clock = t0;
00735 break;
00736 default:
00737 throw "invalid pass request";
00738 }
00739 }
00740 catch(char *msg){
00741 gl_error("sync_group_recorder: %s", msg);
00742 }
00743 catch(const char *msg){
00744 gl_error("sync_group_recorder: %s", msg);
00745 }
00746 return rv;
00747 }
00748
00749 EXPORT int commit_group_recorder(OBJECT *obj){
00750 int rv = 0;
00751 group_recorder *my = OBJECTDATA(obj, group_recorder);
00752 try {
00753 rv = my->commit(obj->clock,0.0,false);
00754 }
00755 catch (char *msg){
00756 gl_error("commit_group_recorder: %s", msg);
00757 }
00758 catch (const char *msg){
00759 gl_error("commit_group_recorder: %s", msg);
00760 }
00761 return rv;
00762 }
00763
00764 EXPORT int isa_group_recorder(OBJECT *obj, char *classname)
00765 {
00766 return OBJECTDATA(obj, group_recorder)->isa(classname);
00767 }
00768
00769
00770 EXPORT SIMULATIONMODE update_group_recorder(OBJECT *obj, TIMESTAMP t0, unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val)
00771 {
00772 double tsdblvalue, dblincrement;
00773 int return_val;
00774 group_recorder *thisrcdr = OBJECTDATA(obj,group_recorder);
00775
00776
00777 if ((iteration_count_val == 0) && (delta_time != 0))
00778 {
00779
00780 dblincrement = ((double)delta_time-(double)dt)/(double)DT_SECOND;
00781
00782
00783 tsdblvalue = (double)t0 + dblincrement;
00784
00785
00786 return_val = thisrcdr->commit(t0,tsdblvalue,true);
00787
00788
00789 if (return_val == 0)
00790 {
00791 return SM_ERROR;
00792 }
00793 }
00794
00795
00796 return SM_EVENT;
00797 }
00798
00799
00800 EXPORT int group_recorder_postroutine(OBJECT *obj, double timedbl)
00801 {
00802 int return_value;
00803 group_recorder *thisrcdr = OBJECTDATA(obj,group_recorder);
00804
00805
00806 return_value = thisrcdr->commit(0,timedbl,true);
00807
00808
00809 return return_value;
00810 }
00811