00001
00019 #include <stdlib.h>
00020 #include <stdio.h>
00021 #include <errno.h>
00022 #include <ctype.h>
00023 #include "gridlabd.h"
00024 #include "object.h"
00025 #include "aggregate.h"
00026
00027 #include "tape.h"
00028 #include "file.h"
00029 #include "odbc.h"
00030
00031 #ifndef WIN32
00032 #define strtok_s strtok_r
00033 #endif
00034
00035 CLASS *recorder_class = NULL;
00036 static OBJECT *last_recorder = NULL;
00037
00038 EXPORT int create_recorder(OBJECT **obj, OBJECT *parent)
00039 {
00040 *obj = gl_create_object(recorder_class);
00041 if (*obj!=NULL)
00042 {
00043 struct recorder *my = OBJECTDATA(*obj,struct recorder);
00044 last_recorder = *obj;
00045 gl_set_parent(*obj,parent);
00046 strcpy(my->file,"");
00047 strcpy(my->multifile,"");
00048 strcpy(my->filetype,"txt");
00049 strcpy(my->delim,",");
00050 strcpy(my->property,"(undefined)");
00051 my->interval = -1;
00052 my->dInterval = -1.0;
00053 my->last.ts = -1;
00054 strcpy(my->last.value,"");
00055 my->limit = 0;
00056 my->samples = 0;
00057 my->status = TS_INIT;
00058 my->trigger[0]='\0';
00059 my->format = 0;
00060 strcpy(my->plotcommands,"");
00061 my->target = gl_get_property(*obj,my->property);
00062 return 1;
00063 }
00064 return 0;
00065 }
00066
00067 static int recorder_open(OBJECT *obj)
00068 {
00069 char32 type="file";
00070 char1024 fname="";
00071 char32 flags="w";
00072 struct recorder *my = OBJECTDATA(obj,struct recorder);
00073
00074 my->interval = (int64)(my->dInterval/TS_SECOND);
00075
00076 if (sscanf(my->file,"%32[^:]:%1024[^:]:%[^:]",type,fname,flags)==1)
00077 {
00078
00079 strcpy(fname,my->file);
00080 strcpy(type,"file");
00081 }
00082
00083
00084 if (strcmp(fname,"")==0)
00085
00086
00087 sprintf(fname,"%s-%d.%s",obj->parent->oclass->name,obj->parent->id, my->filetype);
00088
00089
00090 if(my->type == FT_FILE && my->multifile[0] != 0){
00091 if(my->interval < 1){
00092 gl_error("transient recorders cannot use multi-run output files");
00093 return 0;
00094 }
00095 sprintf(my->multitempfile, "temp_%s", my->file);
00096 my->multifp = fopen(my->multitempfile, "w");
00097 if(my->multifp == NULL){
00098 gl_error("unable to open \'%s\' for multi-run output", my->multitempfile);
00099 } else {
00100 time_t now=time(NULL);
00101
00102 my->inputfp = fopen(my->multifile, "r");
00103
00104
00105 fprintf(my->multifp,"# file...... %s\n", my->file);
00106 fprintf(my->multifp,"# date...... %s", asctime(localtime(&now)));
00107 #ifdef WIN32
00108 fprintf(my->multifp,"# user...... %s\n", getenv("USERNAME"));
00109 fprintf(my->multifp,"# host...... %s\n", getenv("MACHINENAME"));
00110 #else
00111 fprintf(my->multifp,"# user...... %s\n", getenv("USER"));
00112 fprintf(my->multifp,"# host...... %s\n", getenv("HOST"));
00113 #endif
00114 fprintf(my->multifp,"# target.... %s %d\n", obj->parent->oclass->name, obj->parent->id);
00115 fprintf(my->multifp,"# trigger... %s\n", my->trigger[0]=='\0'?"(none)":my->trigger);
00116 fprintf(my->multifp,"# interval.. %d\n", my->interval);
00117 fprintf(my->multifp,"# limit..... %d\n", my->limit);
00118 fprintf(my->multifp,"# property.. %s\n", my->property);
00119
00120 }
00121 if(my->inputfp != NULL){
00122 char1024 inbuffer;
00123 char *data;
00124 int get_col = 0;
00125 do{
00126 if(0 != fgets(inbuffer, 1024, my->inputfp)){
00127 char *end = strchr(inbuffer, '\n');
00128 data = inbuffer+strlen("# file...... ");
00129 if(end != 0){
00130 *end = 0;
00131 }
00132
00133 if(strncmp(inbuffer, "# file", strlen("# file")) == 0){
00134 ;
00135 } else if(strncmp(inbuffer, "# date", strlen("# date")) == 0){
00136 ;
00137 } else if(strncmp(inbuffer, "# user", strlen("# user")) == 0){
00138 ;
00139 } else if(strncmp(inbuffer, "# host", strlen("# host")) == 0){
00140 ;
00141 } else if(strncmp(inbuffer, "# target", strlen("# target")) == 0){
00142
00143 char256 target;
00144 sprintf(target, "%s %d", obj->parent->oclass->name, obj->parent->id);
00145 if(0 != strncmp(target, data, strlen(data))){
00146 gl_error("recorder:%i: re-recording target mismatch: was %s, now %s", obj->id, data, target);
00147 }
00148 } else if(strncmp(inbuffer, "# trigger", strlen("# trigger")) == 0){
00149
00150 ;
00151 } else if(strncmp(inbuffer, "# interval", strlen("# interval")) == 0){
00152
00153 int interval = atoi(data);
00154 if(interval != my->interval){
00155 gl_error("recorder:%i: re-recording interval mismatch: was %i, now %i", obj->id, interval, my->interval);
00156 }
00157 } else if(strncmp(inbuffer, "# limit", strlen("# limit")) == 0){
00158
00159 int limit = atoi(data);
00160 if(limit != my->limit){
00161 gl_error("recorder:%i: re-recording limit mismatch: was %i, now %i", obj->id, limit, my->limit);
00162 }
00163 } else if(strncmp(inbuffer, "# property", strlen("# property")) == 0){
00164
00165 if(0 != strncmp(my->property, data, strlen(my->property))){
00166 gl_error("recorder:%i: re-recording property mismatch: was %s, now %s", obj->id, data, my->property);
00167 }
00168
00169 get_col = 1;
00170 }
00171 } else {
00172 gl_error("error reading multi-read input file \'%s\'", my->multifile);
00173 break;
00174 }
00175 } while(inbuffer[0] == '#' && get_col == 0);
00176
00177 if(0 != fgets(inbuffer, 1024, my->inputfp)){
00178 int rep=0;
00179 int replen = strlen("# repetition");
00180 int len, lenmax = 1024, i = 0;
00181 char1024 propstr, shortstr;
00182 PROPERTY *tprop = my->target;
00183 gl_verbose("read last buffer");
00184 if(strncmp(inbuffer, "# repetition", replen) == 0){
00185 char *trim;
00186 rep = atoi(inbuffer + replen + 1);
00187 ++rep;
00188 fprintf(my->multifp, "# repetition %i\n", rep);
00189 fgets(inbuffer, 1024, my->inputfp);
00190 trim = strchr(inbuffer, '\n');
00191 if(trim) *trim = 0;
00192 } else {
00193 rep = 0;
00194 fprintf(my->multifp, "# repetition %i\n", rep);
00195 }
00196
00197 while(tprop != NULL){
00198 sprintf(shortstr, ",%s(%i)", tprop->name, rep);
00199 len = strlen(shortstr);
00200 if(len > lenmax){
00201 gl_error("multi-run recorder output full property list is larger than the buffer, please start a new file!");
00202 break;
00203 }
00204 strncpy(propstr+i, shortstr, len+1);
00205 i += len;
00206 tprop = tprop->next;
00207 }
00208 fprintf(my->multifp, "%s%s\n", inbuffer, propstr);
00209 }
00210 } else {
00211 char1024 propstr, shortstr;
00212 int len, lenmax = 1024, i = 0;
00213 PROPERTY *tprop = my->target;
00214 fprintf(my->multifp, "# repetition 0\n");
00215
00216 sprintf(propstr, "# timestamp");
00217 len = strlen(propstr);
00218 lenmax-=len;
00219 i = len;
00220
00221 while(tprop != NULL){
00222 sprintf(shortstr, ",%s(0)", tprop->name);
00223 len = strlen(shortstr);
00224 if(len > lenmax){
00225 gl_error("multi-run recorder output full property list is larger than the buffer, please start a new file!");
00226 break;
00227 }
00228 strncpy(propstr+i, shortstr, len+1);
00229 i += len;
00230 tprop = tprop->next;
00231 }
00232 fprintf(my->multifp, "%s\n", propstr);
00233 }
00234 }
00235
00236
00237 my->ops = get_ftable(type)->recorder;
00238 if(my->ops == NULL)
00239 return 0;
00240 return my->ops->open(my, fname, flags);
00241 }
00242
00243 static int write_recorder(struct recorder *my, char *ts, char *value)
00244 {
00245 return my->ops->write(my, ts, value);
00246 }
00247
00248 static void close_recorder(struct recorder *my)
00249 {
00250 if (my->ops){
00251 my->ops->close(my);
00252 }
00253 if(my->multifp){
00254 if(0 != fclose(my->multifp)){
00255 gl_error("unable to close multi-run temp file \'%s\'", my->multitempfile);
00256 perror("fclose(): ");
00257 }
00258
00259 my->multifp = 0;
00260
00261 if(my->inputfp != NULL){
00262 fclose(my->inputfp);
00263 if(0 != remove(my->multifile)){
00264 gl_error("unable to remove out-of-data multi-run file \'%s\'", my->multifile);
00265 perror("remove(): ");
00266 }
00267 }
00268 if(0 != rename(my->multitempfile, my->multifile)){
00269 gl_error("unable to rename multi-run file \'%s\' to \'%s\'", my->multitempfile, my->multifile);
00270 perror("rename(): ");
00271 }
00272
00273 }
00274 }
00275
00276 static TIMESTAMP recorder_write(OBJECT *obj)
00277 {
00278 struct recorder *my = OBJECTDATA(obj,struct recorder);
00279 char ts[64]="0";
00280 if (my->format==0)
00281 {
00282 if (my->last.ts>TS_ZERO)
00283 {
00284 DATETIME dt;
00285 gl_localtime(my->last.ts,&dt);
00286 gl_strtime(&dt,ts,sizeof(ts));
00287 }
00288
00289 }
00290 else
00291 sprintf(ts,"%" FMT_INT64 "d", my->last.ts);
00292 if ((my->limit>0 && my->samples > my->limit)
00293 || write_recorder(my, ts, my->last.value)==0)
00294 {
00295 close_recorder(my);
00296 my->status = TS_DONE;
00297 }
00298 else
00299 my->samples++;
00300
00301
00302
00303
00304 if(my->multifp != NULL){
00305 char1024 inbuffer;
00306 char1024 outbuffer;
00307 char *lasts = 0;
00308 char *in_ts = 0;
00309 char *in_tok = 0;
00310
00311 memset(inbuffer, 0, sizeof(inbuffer));
00312
00313 if(my->inputfp != NULL){
00314
00315 do{
00316 if(0 == fgets(inbuffer, 1024, my->inputfp)){
00317 if(feof(my->inputfp)){
00318
00319
00320
00321 return TS_NEVER;
00322 } else {
00323 gl_error("error reading past recordings file");
00324 my->status = TS_ERROR;
00325 return TS_NEVER;
00326 }
00327 }
00328
00329 } while(inbuffer[0] == '#');
00330
00331
00332
00333 in_ts = strtok_s(inbuffer, ",\n", &lasts);
00334 in_tok = strtok_s(NULL, "\n", &lasts);
00335
00336 if(in_ts == NULL){
00337 gl_error("unable to indentify a timestamp within the line read from ");
00338 }
00339
00340
00341
00342 if(strcmp(in_ts, ts) != 0){
00343 gl_warning("timestamp mismatch between current input line and simulation time");
00344 }
00345 sprintf(outbuffer, "%s,%s", in_tok, my->last.value);
00346 } else {
00347 strcpy(outbuffer, my->last.value);
00348 }
00349
00350 fprintf(my->multifp, "%s,%s\n", ts, outbuffer);
00351 }
00352 return TS_NEVER;
00353 }
00354
00355 PROPERTY *link_properties(OBJECT *obj, char *property_list)
00356 {
00357 char *item;
00358 PROPERTY *first=NULL, *last=NULL;
00359 UNIT *unit = NULL;
00360 char1024 list;
00361 complex oblig;
00362 strcpy(list,property_list);
00363 for (item=strtok(list,","); item!=NULL; item=strtok(NULL,","))
00364 {
00365 char256 pstr, ustr;
00366 char *cpart = 0;
00367 int64 cid = -1;
00368 PROPERTY *prop = NULL;
00369 PROPERTY *target = NULL;
00370 double scale = 1.0;
00371
00372
00373 while (isspace(*item)) item++;
00374 if(2 == sscanf(item,"%[A-Za-z0-9_.][%[^]\n,\0]", pstr, ustr)){
00375 unit = gl_find_unit(ustr);
00376 if(unit == NULL){
00377 gl_error("recorder:%d: unable to find unit '%s' for property '%s'",obj->id, ustr,pstr);
00378 return NULL;
00379 }
00380 item = pstr;
00381 }
00382 prop = (PROPERTY*)malloc(sizeof(PROPERTY));
00383
00384
00385
00386 cpart = strchr(item, '.');
00387 if(cpart != NULL){
00388 if(strcmp("imag", cpart+1) == 0){
00389 cid = (int)((int64)&(oblig.i) - (int64)&oblig);
00390 *cpart = 0;
00391 } else if(strcmp("real", cpart+1) == 0){
00392 cid = (int)((int64)&(oblig.r) - (int64)&oblig);
00393 *cpart = 0;
00394 } else {
00395 ;
00396 }
00397 }
00398
00399 target = gl_get_property(obj,item);
00400
00401 if (prop!=NULL && target!=NULL)
00402 {
00403 if(unit != NULL && target->unit == NULL){
00404 gl_error("recorder:%d: property '%s' is unitless, ignoring unit conversion", obj->id, item);
00405 }
00406 else if(unit != NULL && 0 == gl_convert_ex(target->unit, unit, &scale))
00407 {
00408 gl_error("recorder:%d: unable to convert property '%s' units to '%s'", obj->id, item, ustr);
00409 return NULL;
00410 }
00411 if (first==NULL) first=prop; else last->next=prop;
00412 last=prop;
00413 memcpy(prop,target,sizeof(PROPERTY));
00414 if(unit) prop->unit = unit;
00415 prop->next = NULL;
00416 }
00417 else
00418 {
00419 gl_error("recorder: property '%s' not found", item);
00420 return NULL;
00421 }
00422 if(cid >= 0){
00423 prop->ptype = PT_double;
00424 (prop->addr) = (int64)(prop->addr) + cid;
00425 }
00426 }
00427 return first;
00428 }
00429
00430 int read_properties(OBJECT *obj, PROPERTY *prop, char *buffer, int size)
00431 {
00432 PROPERTY *p;
00433 int offset=0;
00434 int count=0;
00435 for (p=prop; p!=NULL && offset<size-33; p=p->next)
00436 {
00437 if (offset>0) strcpy(buffer+offset++,",");
00438 offset+=gl_get_value(obj,GETADDR(obj,p),buffer+offset,size-offset-1,p);
00439 buffer[offset]='\0';
00440 count++;
00441 }
00442 return count;
00443 }
00444
00445 EXPORT TIMESTAMP sync_recorder(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00446 {
00447 struct recorder *my = OBJECTDATA(obj,struct recorder);
00448 typedef enum {NONE='\0', LT='<', EQ='=', GT='>'} COMPAREOP;
00449 COMPAREOP comparison;
00450 char1024 buffer = "";
00451
00452 if (my->status==TS_DONE)
00453 {
00454 close_recorder(my);
00455 return TS_NEVER;
00456 }
00457
00458 if(obj->parent == NULL){
00459 char tb[32];
00460 sprintf(buffer, "'%s' lacks a parent object", obj->name ? obj->name : tb, sprintf(tb, "recorder:%i", obj->id));
00461 close_recorder(my);
00462 my->status = TS_ERROR;
00463 goto Error;
00464 }
00465
00466 if(my->last.ts < 1 && my->interval != -1)
00467 my->last.ts = t0;
00468
00469
00470 if (my->target==NULL){
00471 my->target = link_properties(obj->parent,my->property);
00472 }
00473 if (my->target==NULL)
00474 {
00475 sprintf(buffer,"'%s' contains a property of %s %d that is not found", my->property, obj->parent->oclass->name, obj->parent->id);
00476 close_recorder(my);
00477 my->status = TS_ERROR;
00478 goto Error;
00479 }
00480
00481
00482 if ((my->status==TS_OPEN) && (t0 > obj->clock))
00483 {
00484 obj->clock = t0;
00485
00486 if((my->interval > 0) && (my->last.ts < t0) && (my->last.value[0] != 0)){
00487 recorder_write(obj);
00488 my->last.value[0] = 0;
00489 }
00490 }
00491
00492
00493 if ((my->target != NULL) && (my->interval == 0 || my->interval == -1)){
00494 if(read_properties(obj->parent,my->target,buffer,sizeof(buffer))==0)
00495 {
00496 sprintf(buffer,"unable to read property '%s' of %s %d", my->property, obj->parent->oclass->name, obj->parent->id);
00497 close_recorder(my);
00498 my->status = TS_ERROR;
00499 }
00500 }
00501 if ((my->target != NULL) && (my->interval > 0)){
00502 if((t0 >=my->last.ts + my->interval) || (t0 == my->last.ts)){
00503 if(read_properties(obj->parent,my->target,buffer,sizeof(buffer))==0)
00504 {
00505 sprintf(buffer,"unable to read property '%s' of %s %d", my->property, obj->parent->oclass->name, obj->parent->id);
00506 close_recorder(my);
00507 my->status = TS_ERROR;
00508 }
00509 my->last.ts = t0;
00510 }
00511 }
00512
00513
00514 comparison = (COMPAREOP)my->trigger[0];
00515 if (comparison!=NONE)
00516 {
00517 int desired = comparison==LT ? -1 : (comparison==EQ ? 0 : (comparison==GT ? 1 : -2));
00518
00519
00520 int actual = strcmp(buffer,my->trigger+1);
00521 if (actual!=desired || (my->status==TS_INIT && !recorder_open(obj)))
00522 {
00523
00524 return (my->interval==0 || my->interval==-1) ? TS_NEVER : t0+my->interval;
00525 }
00526 }
00527 else if (my->status==TS_INIT && !recorder_open(obj))
00528 {
00529 close_recorder(my);
00530 return TS_NEVER;
00531 }
00532
00533 if(my->last.ts < 1 && my->interval != -1)
00534 my->last.ts = t0;
00535
00536
00537 if (my->status==TS_OPEN)
00538 {
00539 if (my->interval==0
00540 || ((my->interval==-1) && my->last.ts!=t0 && strcmp(buffer,my->last.value)!=0)
00541 )
00542
00543 {
00544 strncpy(my->last.value,buffer,sizeof(my->last.value));
00545 my->last.ts = t0;
00546 recorder_write(obj);
00547 } else if (my->interval > 0 && my->last.ts == t0){
00548 strncpy(my->last.value,buffer,sizeof(my->last.value));
00549 }
00550 }
00551 Error:
00552 if (my->status==TS_ERROR)
00553 {
00554 gl_error("recorder %d %s\n",obj->id, buffer);
00555 close_recorder(my);
00556 my->status=TS_DONE;
00557 return TS_NEVER;
00558 }
00559
00560 if (my->interval==0 || my->interval==-1)
00561 {
00562 return TS_NEVER;
00563 }
00564 else
00565 return my->last.ts+my->interval;
00566 }
00567