X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Frrd_graph.c;h=27314776c0d3d9e152bd83a4f332c6818e3b341d;hb=e882b59504803ffe62eaeff5efaf0ff730d85744;hp=f42acaf397b1980f5c966795f92b2a76bdc16140;hpb=7c016dfa001ae254bf4e18126f814ee8f0abd821;p=rrdtool.git diff --git a/src/rrd_graph.c b/src/rrd_graph.c index f42acaf..2731477 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -4,7 +4,10 @@ * rrd__graph.c make creates ne rrds ****************************************************************************/ +#if 0 #include "rrd_tool.h" +#endif + #include #include #include @@ -14,6 +17,9 @@ #include #endif +#include "rrd_graph.h" +#include "rrd_graph_helper.h" + #define SmallFont gdLucidaNormal10 #define LargeFont gdLucidaBold12 @@ -25,58 +31,6 @@ # define DPRINT(x) #endif -#define DEF_NAM_FMT "%29[_A-Za-z0-9]" - -enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY, - TMT_WEEK,TMT_MONTH,TMT_YEAR}; - -enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB, - GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__}; - - -enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1, - GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,GF_DEF, GF_CDEF}; - -enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF, - OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD, - OP_SUB,OP_MUL, - OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP, - OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF, - OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL, - OP_UN,OP_END}; - -enum if_en {IF_GIF=0,IF_PNG=1}; - -typedef struct rpnp_t { - enum op_en op; - double val; /* value for a OP_NUMBER */ - long ptr; /* pointer into the gdes array for OP_VAR */ - double *data; /* pointer to the current value from OP_VAR DAS*/ - long ds_cnt; /* data source count for data pointer */ - long step; /* time step for OP_VAR das */ -} rpnp_t; - - -typedef struct col_trip_t { - int red; /* red = -1 is no color */ - int green; - int blue; - int i; /* color index assigned in gif image i=-1 is unasigned*/ -} col_trip_t; - - -typedef struct xlab_t { - long minsec; /* minimum sec per pix */ - enum tmt_en gridtm; /* grid interval in what ?*/ - long gridst; /* how many whats per grid*/ - enum tmt_en mgridtm; /* label interval in what ?*/ - long mgridst; /* how many whats per label*/ - enum tmt_en labtm; /* label interval in what ?*/ - long labst; /* how many whats per label*/ - long precis; /* label precision -> label placement*/ - char *stst; /* strftime string*/ -} xlab_t; - xlab_t xlab[] = { {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, @@ -111,11 +65,6 @@ double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sensible y label intervals ...*/ -typedef struct ylab_t { - double grid; /* grid spacing */ - int lfac[4]; /* associated label spacing*/ -} ylab_t; - ylab_t ylab[]= { {0.1, {1,2, 5,10}}, {0.2, {1,5,10,20}}, @@ -145,130 +94,6 @@ col_trip_t graph_col[] = { /* default colors */ {255,0,0,-1} /*arrow*/ }; -/* this structure describes the elements which can make up a graph. - because they are quite diverse, not all elements will use all the - possible parts of the structure. */ -#ifdef HAVE_SNPRINTF -#define FMT_LEG_LEN 200 -#else -#define FMT_LEG_LEN 2000 -#endif - -typedef struct graph_desc_t { - enum gf_en gf; /* graphing function */ - char vname[30]; /* name of the variable */ - long vidx; /* gdes reference */ - char rrd[255]; /* name of the rrd_file containing data */ - char ds_nam[DS_NAM_SIZE]; /* data source name */ - long ds; /* data source number */ - enum cf_en cf; /* consolidation function */ - col_trip_t col; /* graph color */ - char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */ - char legend[FMT_LEG_LEN+5]; /* legend*/ - gdPoint legloc; /* location of legend */ - double yrule; /* value for y rule line */ - time_t xrule; /* value for x rule line */ - rpnp_t *rpnp; /* instructions for CDEF function */ - - /* description of data fetched for the graph element */ - time_t start,end; /* timestaps for first and last data element */ - unsigned long step; /* time between samples */ - unsigned long ds_cnt; /* how many data sources are there in the fetch */ - long data_first; /* first pointer to this data */ - char **ds_namv; /* name of datasources in the fetch. */ - rrd_value_t *data; /* the raw data drawn from the rrd */ - rrd_value_t *p_data; /* processed data, xsize elments */ - -} graph_desc_t; - -#define ALTYGRID 0x01 /* use alternative y grid algorithm */ -#define ALTAUTOSCALE 0x02 /* use alternative algorithm to find lower and upper bounds */ -#define ALTAUTOSCALE_MAX 0x04 /* use alternative algorithm to find upper bounds */ -#define NOLEGEND 0x08 /* use no legend */ - -typedef struct image_desc_t { - - /* configuration of graph */ - - char graphfile[MAXPATH]; /* filename for graphic */ - long xsize,ysize; /* graph area size in pixels */ - col_trip_t graph_col[__GRC_END__]; /* real colors for the graph */ - char ylegend[200]; /* legend along the yaxis */ - char title[200]; /* title for graph */ - int draw_x_grid; /* no x-grid at all */ - int draw_y_grid; /* no x-grid at all */ - xlab_t xlab_user; /* user defined labeling for xaxis */ - char xlab_form[200]; /* format for the label on the xaxis */ - - double ygridstep; /* user defined step for y grid */ - int ylabfact; /* every how many y grid shall a label be written ? */ - - time_t start,end; /* what time does the graph cover */ - unsigned long step; /* any preference for the default step ? */ - rrd_value_t minval,maxval; /* extreme values in the data */ - int rigid; /* do not expand range even with - values outside */ - char* imginfo; /* construct an xorigin \ @@ -324,8 +149,6 @@ ytr(image_desc_t *im, double value){ } } - - /* conversion function for symbolic entry names */ @@ -349,6 +172,7 @@ enum gf_en gf_conv(char *string){ conv_if(TICK,GF_TICK) conv_if(DEF,GF_DEF) conv_if(CDEF,GF_CDEF) + conv_if(VDEF,GF_VDEF) return (-1); } @@ -424,7 +248,7 @@ auto_scale( ) { - char *symbol[] = {"a", /* 10e-18 Ato */ + char *symbol[] = {"a", /* 10e-18 Atto */ "f", /* 10e-15 Femto */ "p", /* 10e-12 Pico */ "n", /* 10e-9 Nano */ @@ -434,7 +258,7 @@ auto_scale( "k", /* 10e3 Kilo */ "M", /* 10e6 Mega */ "G", /* 10e9 Giga */ - "T", /* 10e12 Terra */ + "T", /* 10e12 Tera */ "P", /* 10e15 Peta */ "E"};/* 10e18 Exa */ @@ -465,7 +289,7 @@ si_unit( ) { - char symbol[] = {'a', /* 10e-18 Ato */ + char symbol[] = {'a', /* 10e-18 Atto */ 'f', /* 10e-15 Femto */ 'p', /* 10e-12 Pico */ 'n', /* 10e-9 Nano */ @@ -475,7 +299,7 @@ si_unit( 'k', /* 10e3 Kilo */ 'M', /* 10e6 Mega */ 'G', /* 10e9 Giga */ - 'T', /* 10e12 Terra */ + 'T', /* 10e12 Tera */ 'P', /* 10e15 Peta */ 'E'};/* 10e18 Exa */ @@ -688,18 +512,18 @@ reduce_data( if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col]; else { switch (cf) { - case CF_HWPREDICT: - case CF_DEVSEASONAL: - case CF_DEVPREDICT: - case CF_SEASONAL: + case CF_HWPREDICT: + case CF_DEVSEASONAL: + case CF_DEVPREDICT: + case CF_SEASONAL: case CF_AVERAGE: newval += srcptr[i*(*ds_cnt)+col]; break; case CF_MINIMUM: newval = min (newval,srcptr[i*(*ds_cnt)+col]); break; - case CF_FAILURES: - /* an interval contains a failure if any subintervals contained a failure */ + case CF_FAILURES: + /* an interval contains a failure if any subintervals contained a failure */ case CF_MAXIMUM: newval = max (newval,srcptr[i*(*ds_cnt)+col]); break; @@ -711,15 +535,15 @@ reduce_data( } if (validval == 0){newval = DNAN;} else{ switch (cf) { - case CF_HWPREDICT: - case CF_DEVSEASONAL: - case CF_DEVPREDICT: - case CF_SEASONAL: - case CF_AVERAGE: - newval /= validval; + case CF_HWPREDICT: + case CF_DEVSEASONAL: + case CF_DEVPREDICT: + case CF_SEASONAL: + case CF_AVERAGE: + newval /= validval; break; case CF_MINIMUM: - case CF_FAILURES: + case CF_FAILURES: case CF_MAXIMUM: case CF_LAST: break; @@ -822,12 +646,19 @@ data_fetch( image_desc_t *im ) * CDEF stuff *************************************************************/ +long +find_var_wrapper(void *arg1, char *key) +{ + return find_var((image_desc_t *) arg1, key); +} + /* find gdes containing var*/ long find_var(image_desc_t *im, char *key){ long ii; for(ii=0;iigdes_c-1;ii++){ if((im->gdes[ii].gf == GF_DEF + || im->gdes[ii].gf == GF_VDEF || im->gdes[ii].gf == GF_CDEF) && (strcmp(im->gdes[ii].vname,key) == 0)){ return ii; @@ -853,536 +684,163 @@ lcd(long *num){ return num[i]; } - -/* convert string to rpnp */ -rpnp_t * -str2rpn(image_desc_t *im,char *expr){ - int pos=0; - long steps=-1; - rpnp_t *rpnp; - char vname[30]; - - rpnp=NULL; - - while(*expr){ - if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* - sizeof(rpnp_t)))==NULL){ - return NULL; - } - - else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) - && (expr[pos] == ',')){ - rpnp[steps].op = OP_NUMBER; - expr+=pos; - } - -#define match_op(VV,VVV) \ - else if (strncmp(expr, #VVV, strlen(#VVV))==0 && \ - (expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0') ){ \ - rpnp[steps].op = VV; \ - expr+=strlen(#VVV); \ - } - - match_op(OP_ADD,+) - match_op(OP_SUB,-) - match_op(OP_MUL,*) - match_op(OP_DIV,/) - match_op(OP_MOD,%) - match_op(OP_SIN,SIN) - match_op(OP_COS,COS) - match_op(OP_LOG,LOG) - match_op(OP_FLOOR,FLOOR) - match_op(OP_CEIL,CEIL) - match_op(OP_EXP,EXP) - match_op(OP_DUP,DUP) - match_op(OP_EXC,EXC) - match_op(OP_POP,POP) - match_op(OP_LT,LT) - match_op(OP_LE,LE) - match_op(OP_GT,GT) - match_op(OP_GE,GE) - match_op(OP_EQ,EQ) - match_op(OP_IF,IF) - match_op(OP_MIN,MIN) - match_op(OP_MAX,MAX) - match_op(OP_LIMIT,LIMIT) - /* order is important here ! .. match longest first */ - match_op(OP_UNKN,UNKN) - match_op(OP_UN,UN) - match_op(OP_NEGINF,NEGINF) - match_op(OP_PREV,PREV) - match_op(OP_INF,INF) - match_op(OP_NOW,NOW) - match_op(OP_LTIME,LTIME) - match_op(OP_TIME,TIME) - - -#undef match_op - - - else if ((sscanf(expr,DEF_NAM_FMT "%n", - vname,&pos) == 1) - && ((rpnp[steps].ptr = find_var(im,vname)) != -1)){ - rpnp[steps].op = OP_VARIABLE; - expr+=pos; - } - - else { - free(rpnp); - return NULL; - } - if (*expr == 0) - break; - if (*expr == ',') - expr++; - else { - free(rpnp); - return NULL; - } - } - rpnp[steps+1].op = OP_END; - return rpnp; -} - -/* figure out what the local timezone offset for any point in - time was. Return it in seconds */ - -int -tzoffset( time_t now ){ - int gm_sec, gm_min, gm_hour, gm_yday, gm_year, - l_sec, l_min, l_hour, l_yday, l_year; - struct tm *t; - int off; - t = gmtime(&now); - gm_sec = t->tm_sec; - gm_min = t->tm_min; - gm_hour = t->tm_hour; - gm_yday = t->tm_yday; - gm_year = t->tm_year; - t = localtime(&now); - l_sec = t->tm_sec; - l_min = t->tm_min; - l_hour = t->tm_hour; - l_yday = t->tm_yday; - l_year = t->tm_year; - off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; - if ( l_yday > gm_yday || l_year > gm_year){ - off += 24*3600; - } else if ( l_yday < gm_yday || l_year < gm_year){ - off -= 24*3600; - } - - return off; -} - - - -#define dc_stackblock 100 - -/* run the rpn calculator on all the CDEF arguments */ - +/* run the rpn calculator on all the VDEF and CDEF arguments */ int data_calc( image_desc_t *im){ - int gdi,rpi; + int gdi; int dataidx; - long *steparray; + long *steparray, rpi; int stepcnt; time_t now; - double *stack = NULL; - long dc_stacksize = 0; - - for (gdi=0;gdigdes_c;gdi++){ - /* only GF_CDEF elements are of interest */ - if (im->gdes[gdi].gf != GF_CDEF) - continue; - im->gdes[gdi].ds_cnt = 1; - im->gdes[gdi].ds = 0; - im->gdes[gdi].data_first = 1; - im->gdes[gdi].start = 0; - im->gdes[gdi].end = 0; - steparray=NULL; - stepcnt = 0; - dataidx=-1; - - /* find the variables in the expression. And calc the lowest - common denominator of all step sizes of the data sources involved. - this will be the step size for the cdef created data source*/ - - for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){ - if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){ - long ptr = im->gdes[gdi].rpnp[rpi].ptr; - if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){ - rrd_set_error("realloc steparray"); - free(stack); - return -1; - }; - - - steparray[stepcnt-1] = im->gdes[ptr].step; - - /* adjust start and end of cdef (gdi) so that it runs from - the latest start point to the earliest endpoint of any of the - rras involved (ptr) */ + rpnstack_t rpnstack; - if(im->gdes[gdi].start < im->gdes[ptr].start) - im->gdes[gdi].start = im->gdes[ptr].start; + rpnstack_init(&rpnstack); - if(im->gdes[gdi].end == 0 - || im->gdes[gdi].end > im->gdes[ptr].end) - im->gdes[gdi].end = im->gdes[ptr].end; + for (gdi=0;gdigdes_c;gdi++){ + /* Look for GF_VDEF and GF_CDEF in the same loop, + * so CDEFs can use VDEFs and vice versa + */ + switch (im->gdes[gdi].gf) { + case GF_VDEF: + /* A VDEF has no DS. This also signals other parts + * of rrdtool that this is a VDEF value, not a CDEF. + */ + im->gdes[gdi].ds_cnt = 0; + if (vdef_calc(im,gdi)) { + rrd_set_error("Error processing VDEF '%s'" + ,im->gdes[gdi].vname + ); + rpnstack_free(&rpnstack); + return -1; + } + break; + case GF_CDEF: + im->gdes[gdi].ds_cnt = 1; + im->gdes[gdi].ds = 0; + im->gdes[gdi].data_first = 1; + im->gdes[gdi].start = 0; + im->gdes[gdi].end = 0; + steparray=NULL; + stepcnt = 0; + dataidx=-1; + + /* Find the variables in the expression. + * - VDEF variables are substituted by their values + * and the opcode is changed into OP_NUMBER. +****************** +* Note to Jake: I cannot oversee the implications for your +* COMPUTE DS stuff. Please check if VDEF and COMPUTE are +* compatible (or can be made so). +****************** + * - CDEF variables are analized for their step size, + * the lowest common denominator of all the step + * sizes of the data sources involved is calculated + * and the resulting number is the step size for the + * resulting data source. + */ + for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){ + if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){ + long ptr = im->gdes[gdi].rpnp[rpi].ptr; + if (im->gdes[ptr].ds_cnt == 0) { +#if 0 +printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n", + im->gdes[gdi].vname, + im->gdes[ptr].vname); +printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val); +#endif + im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val; + im->gdes[gdi].rpnp[rpi].op = OP_NUMBER; + } else { + if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){ + rrd_set_error("realloc steparray"); + rpnstack_free(&rpnstack); + return -1; + }; + + steparray[stepcnt-1] = im->gdes[ptr].step; + + /* adjust start and end of cdef (gdi) so + * that it runs from the latest start point + * to the earliest endpoint of any of the + * rras involved (ptr) + */ + if(im->gdes[gdi].start < im->gdes[ptr].start) + im->gdes[gdi].start = im->gdes[ptr].start; + + if(im->gdes[gdi].end == 0 || + im->gdes[gdi].end > im->gdes[ptr].end) + im->gdes[gdi].end = im->gdes[ptr].end; - /* store pointer to the first element of the rra providing - data for variable, further save step size and data source count - of this rra*/ - im->gdes[gdi].rpnp[rpi].data = - im->gdes[ptr].data + im->gdes[ptr].ds; - im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step; - im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt; - } - - } - if(steparray == NULL){ - rrd_set_error("rpn expressions without variables are not supported"); - free(stack); - return -1; - } - steparray[stepcnt]=0; - /* now find the step for the result of the cdef. so that we land on - each step in all of the variables rras */ - - im->gdes[gdi].step = lcd(steparray); - - - free(steparray); - - if((im->gdes[gdi].data = malloc(((im->gdes[gdi].end - -im->gdes[gdi].start) + /* store pointer to the first element of + * the rra providing data for variable, + * further save step size and data source + * count of this rra + */ + im->gdes[gdi].rpnp[rpi].data = + im->gdes[ptr].data + im->gdes[ptr].ds; + im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step; + im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt; + + /* backoff the *.data ptr; this is done so + * rpncalc() function doesn't have to treat + * the first case differently + */ + im->gdes[gdi].rpnp[rpi].data-=im->gdes[ptr].ds_cnt; + } /* if ds_cnt != 0 */ + } /* if OP_VARIABLE */ + } /* loop through all rpi */ + + if(steparray == NULL){ + rrd_set_error("rpn expressions without DEF" + " or CDEF variables are not supported"); + rpnstack_free(&rpnstack); + return -1; + } + steparray[stepcnt]=0; + /* Now find the resulting step. All steps in all + * used RRAs have to be visited + */ + im->gdes[gdi].step = lcd(steparray); + free(steparray); + if((im->gdes[gdi].data = malloc(( + (im->gdes[gdi].end-im->gdes[gdi].start) / im->gdes[gdi].step +1) * sizeof(double)))==NULL){ - rrd_set_error("malloc im->gdes[gdi].data"); - free(stack); - return -1; - } - - /* step through the new cdef results array and calculate the values */ - for (now = im->gdes[gdi].start; - now<=im->gdes[gdi].end; - now += im->gdes[gdi].step){ - long stptr=-1; - /* process each op from the rpn in turn */ - for (rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){ - if (stptr +5 > dc_stacksize){ - dc_stacksize += dc_stackblock; - stack = rrd_realloc(stack,dc_stacksize*sizeof(*stack)); - if (stack==NULL){ - rrd_set_error("RPN stack overflow"); - return -1; - } + rrd_set_error("malloc im->gdes[gdi].data"); + rpnstack_free(&rpnstack); + return -1; } - switch (im->gdes[gdi].rpnp[rpi].op){ - case OP_NUMBER: - stack[++stptr] = im->gdes[gdi].rpnp[rpi].val; - break; - case OP_VARIABLE: - /* make sure we pull the correct value from the *.data array */ - /* adjust the pointer into the array acordingly. */ - if(now > im->gdes[gdi].start && - now % im->gdes[gdi].rpnp[rpi].step == 0){ - im->gdes[gdi].rpnp[rpi].data += - im->gdes[gdi].rpnp[rpi].ds_cnt; - } - stack[++stptr] = *im->gdes[gdi].rpnp[rpi].data; - break; - case OP_PREV: - if (dataidx <= 0) { - stack[++stptr] = DNAN; - } else { - stack[++stptr] = im->gdes[gdi].data[dataidx]; - } - break; - case OP_UNKN: - stack[++stptr] = DNAN; - break; - case OP_INF: - stack[++stptr] = DINF; - break; - case OP_NEGINF: - stack[++stptr] = -DINF; - break; - case OP_NOW: - stack[++stptr] = (double)time(NULL); - break; - case OP_TIME: - stack[++stptr] = (double)now; - break; - case OP_LTIME: - stack[++stptr] = (double)tzoffset(now)+(double)now; - break; - case OP_ADD: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] + stack[stptr]; - stptr--; - break; - case OP_SUB: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] - stack[stptr]; - stptr--; - break; - case OP_MUL: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] * stack[stptr]; - stptr--; - break; - case OP_DIV: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = stack[stptr-1] / stack[stptr]; - stptr--; - break; - case OP_MOD: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-1] = fmod(stack[stptr-1],stack[stptr]); - stptr--; - break; - case OP_SIN: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = sin(stack[stptr]); - break; - case OP_COS: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = cos(stack[stptr]); - break; - case OP_CEIL: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = ceil(stack[stptr]); - break; - case OP_FLOOR: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = floor(stack[stptr]); - break; - case OP_LOG: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = log(stack[stptr]); - break; - case OP_DUP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr+1] = stack[stptr]; - stptr++; - break; - case OP_POP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stptr--; - break; - case OP_EXC: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } else { - double dummy; - dummy = stack[stptr] ; - stack[stptr] = stack[stptr-1]; - stack[stptr-1] = dummy; - } - break; - case OP_EXP: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr] = exp(stack[stptr]); - break; - case OP_LT: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] < stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_LE: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] <= stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_GT: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] > stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_GE: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] >= stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_EQ: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1]) || isnan(stack[stptr])) - stack[stptr-1] = 0.0; - else - stack[stptr-1] = stack[stptr-1] == stack[stptr] ? 1.0 : 0.0; - stptr--; - break; - case OP_IF: - if(stptr<2){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - stack[stptr-2] = stack[stptr-2] != 0.0 ? stack[stptr-1] : stack[stptr]; - stptr--; - stptr--; - break; - case OP_MIN: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1])) - ; - else if (isnan(stack[stptr])) - stack[stptr-1] = stack[stptr]; - else if (stack[stptr-1] > stack[stptr]) - stack[stptr-1] = stack[stptr]; - stptr--; - break; - case OP_MAX: - if(stptr<1){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-1])) - ; - else if (isnan(stack[stptr])) - stack[stptr-1] = stack[stptr]; - else if (stack[stptr-1] < stack[stptr]) - stack[stptr-1] = stack[stptr]; - stptr--; - break; - case OP_LIMIT: - if(stptr<2){ - rrd_set_error("RPN stack underflow"); - free(stack); - return -1; - } - if (isnan(stack[stptr-2])) - ; - else if (isnan(stack[stptr-1])) - stack[stptr-2] = stack[stptr-1]; - else if (isnan(stack[stptr])) - stack[stptr-2] = stack[stptr]; - else if (stack[stptr-2] < stack[stptr-1]) - stack[stptr-2] = DNAN; - else if (stack[stptr-2] > stack[stptr]) - stack[stptr-2] = DNAN; - stptr-=2; - break; - case OP_UN: - if(stptr<0){ - rrd_set_error("RPN stack underflow"); - free(stack); + + /* Step through the new cdef results array and + * calculate the values + */ + for (now = im->gdes[gdi].start; + now<=im->gdes[gdi].end; + now += im->gdes[gdi].step) + { + rpnp_t *rpnp = im -> gdes[gdi].rpnp; + + /* 3rd arg of rpn_calc is for OP_VARIABLE lookups; + * in this case we are advancing by timesteps; + * we use the fact that time_t is a synonym for long + */ + if (rpn_calc(rpnp,&rpnstack,(long) now, + im->gdes[gdi].data,++dataidx) == -1) { + /* rpn_calc sets the error string */ + rpnstack_free(&rpnstack); return -1; - } - stack[stptr] = isnan(stack[stptr]) ? 1.0 : 0.0; - break; - case OP_END: - break; - } - } - if(stptr!=0){ - rrd_set_error("RPN final stack size != 1"); - free(stack); - return -1; - } - im->gdes[gdi].data[++dataidx] = stack[0]; + } + } /* enumerate over time steps within a CDEF */ + break; + default: + continue; } - } - free(stack); + } /* enumerate over CDEFs */ + rpnstack_free(&rpnstack); return 0; } -#undef dc_stacksize - /* massage data so, that we get one value for each x coordinate in the graph */ int data_proc( image_desc_t *im ){ @@ -1462,6 +920,7 @@ data_proc( image_desc_t *im ){ case GF_VRULE: case GF_DEF: case GF_CDEF: + case GF_VDEF: break; } } @@ -1665,49 +1124,59 @@ print_calc(image_desc_t *im, char ***prdata) return 0; } case GF_GPRINT: + /* PRINT and GPRINT can now print VDEF generated values. + * There's no need to do any calculations on them as these + * calculations were already made. + */ vidx = im->gdes[i].vidx; - max_ii =((im->gdes[vidx].end - - im->gdes[vidx].start) - /im->gdes[vidx].step - *im->gdes[vidx].ds_cnt); - printval = DNAN; - validsteps = 0; - for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt; - ii < max_ii+im->gdes[vidx].ds_cnt; - ii+=im->gdes[vidx].ds_cnt){ - if (! finite(im->gdes[vidx].data[ii])) - continue; - if (isnan(printval)){ - printval = im->gdes[vidx].data[ii]; - validsteps++; - continue; - } + if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */ + printval = im->gdes[vidx].vf.val; + } else { /* need to calculate max,min,avg etcetera */ + max_ii =((im->gdes[vidx].end + - im->gdes[vidx].start) + / im->gdes[vidx].step + * im->gdes[vidx].ds_cnt); + printval = DNAN; + validsteps = 0; + for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt; + ii < max_ii+im->gdes[vidx].ds_cnt; + ii+=im->gdes[vidx].ds_cnt){ + if (! finite(im->gdes[vidx].data[ii])) + continue; + if (isnan(printval)){ + printval = im->gdes[vidx].data[ii]; + validsteps++; + continue; + } - switch (im->gdes[i].cf){ - case CF_HWPREDICT: - case CF_DEVPREDICT: - case CF_DEVSEASONAL: - case CF_SEASONAL: - case CF_AVERAGE: - validsteps++; - printval += im->gdes[vidx].data[ii]; - break; - case CF_MINIMUM: - printval = min( printval, im->gdes[vidx].data[ii]); - break; - case CF_FAILURES: - case CF_MAXIMUM: - printval = max( printval, im->gdes[vidx].data[ii]); - break; - case CF_LAST: - printval = im->gdes[vidx].data[ii]; + switch (im->gdes[i].cf){ + case CF_HWPREDICT: + case CF_DEVPREDICT: + case CF_DEVSEASONAL: + case CF_SEASONAL: + case CF_AVERAGE: + validsteps++; + printval += im->gdes[vidx].data[ii]; + break; + case CF_MINIMUM: + printval = min( printval, im->gdes[vidx].data[ii]); + break; + case CF_FAILURES: + case CF_MAXIMUM: + printval = max( printval, im->gdes[vidx].data[ii]); + break; + case CF_LAST: + printval = im->gdes[vidx].data[ii]; + } } - } - if (im->gdes[i].cf == CF_AVERAGE || im -> gdes[i].cf > CF_LAST) { - if (validsteps > 1) { - printval = (printval / validsteps); + if (im->gdes[i].cf==CF_AVERAGE || im->gdes[i].cf > CF_LAST) { + if (validsteps > 1) { + printval = (printval / validsteps); + } } - } + } /* prepare printval */ + + if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) { /* Magfact is set to -1 upon entry to print_calc. If it * is still less than 0, then we need to run auto_scale. @@ -1722,10 +1191,10 @@ print_calc(image_desc_t *im, char ***prdata) printval /= magfact; } *(++percent_s) = 's'; - } - else if (strstr(im->gdes[i].format,"%s") != NULL) { + } else if (strstr(im->gdes[i].format,"%s") != NULL) { auto_scale(im,&printval,&si_symb,&magfact); } + if (im->gdes[i].gf == GF_PRINT){ (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char)); if (bad_format(im->gdes[i].format)) { @@ -1766,6 +1235,7 @@ print_calc(image_desc_t *im, char ***prdata) break; case GF_DEF: case GF_CDEF: + case GF_VDEF: break; } } @@ -2493,7 +1963,7 @@ graph_paint(image_desc_t *im, char ***calcpr) if(data_fetch(im)==-1) return -1; - /* evaluate CDEF operations ... */ + /* evaluate VDEF and CDEF operations ... */ if(data_calc(im)==-1) return -1; @@ -2607,6 +2077,7 @@ graph_paint(image_desc_t *im, char ***calcpr) switch(im->gdes[i].gf){ case GF_CDEF: + case GF_VDEF: case GF_DEF: case GF_PRINT: case GF_GPRINT: @@ -2706,6 +2177,10 @@ graph_paint(image_desc_t *im, char ***calcpr) switch(im->gdes[i].gf){ case GF_HRULE: +printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule); + if(isnan(im->gdes[i].yrule)) { /* fetch variable */ + im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val; + }; if(im->gdes[i].yrule >= im->minval && im->gdes[i].yrule <= im->maxval) gdImageLine(gif, @@ -2714,13 +2189,16 @@ graph_paint(image_desc_t *im, char ***calcpr) im->gdes[i].col.i); break; case GF_VRULE: - if(im->gdes[i].xrule >= im->start - && im->gdes[i].xrule <= im->end) - gdImageLine(gif, + if(im->gdes[i].xrule == 0) { /* fetch variable */ + im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when; + }; + if(im->gdes[i].xrule >= im->start + && im->gdes[i].xrule <= im->end) + gdImageLine(gif, xtr(im,im->gdes[i].xrule),im->yorigin, xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize, im->gdes[i].col.i); - break; + break; default: break; } @@ -3119,7 +2597,7 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize) for(i=optind+1;iFMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ; strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]); break; case GF_HRULE: - if(sscanf( - &argv[i][argstart], - "%lf#%2x%2x%2x:%n", - &im.gdes[im.gdes_c-1].yrule, - &col_red,&col_green,&col_blue, - &strstart) >= 4){ - im.gdes[im.gdes_c-1].col.red = col_red; - im.gdes[im.gdes_c-1].col.green = col_green; - im.gdes[im.gdes_c-1].col.blue = col_blue; - if(strstart <= 0){ - im.gdes[im.gdes_c-1].legend[0] = '\0'; - } else { - scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend); + /* scan for either "HRULE:vname#..." or "HRULE:num#..." + * + * If a vname is used, the value NaN is set; this is catched + * when graphing. Setting value NaN from the script is not + * permitted + */ + strstart=0; + sscanf(&argv[i][argstart], "%lf#%n" + ,&im.gdes[im.gdes_c-1].yrule + ,&strstart + ); + if (strstart==0) { /* no number, should be vname */ + sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n" + ,varname + ,&strstart + ); + if (strstart) { + im.gdes[im.gdes_c-1].yrule = DNAN;/* signal use of vname */ + if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){ + im_free(&im); + rrd_set_error("unknown variable '%s' in HRULE",varname); + return -1; + } + if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) { + im_free(&im); + rrd_set_error("Only VDEF is allowed in HRULE",varname); + return -1; + } } } else { +printf("DEBUG: matched HRULE:num\n"); +printf("DEBUG: strstart==%i\n",strstart); + }; + if (strstart==0) { im_free(&im); rrd_set_error("can't parse '%s'",&argv[i][argstart]); return -1; - } + } else { + int n=0; + if(sscanf( + &argv[i][argstart+strstart], + "%2x%2x%2x:%n", + &col_red, + &col_green, + &col_blue, + &n)>=3) { + im.gdes[im.gdes_c-1].col.red = col_red; + im.gdes[im.gdes_c-1].col.green = col_green; + im.gdes[im.gdes_c-1].col.blue = col_blue; + if (n==0) { + im.gdes[im.gdes_c-1].legend[0] = '\0'; + } else { + scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend); + } + } else { + im_free(&im); + rrd_set_error("can't parse '%s'",&argv[i][argstart]); + return -1; + } + } + break; case GF_VRULE: - if(sscanf( - &argv[i][argstart], - "%lu#%2x%2x%2x:%n", - &im.gdes[im.gdes_c-1].xrule, - &col_red, - &col_green, - &col_blue, - &strstart) >= 4){ - im.gdes[im.gdes_c-1].col.red = col_red; - im.gdes[im.gdes_c-1].col.green = col_green; - im.gdes[im.gdes_c-1].col.blue = col_blue; - if(strstart <= 0){ - im.gdes[im.gdes_c-1].legend[0] = '\0'; - } else { - scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend); + /* scan for either "VRULE:vname#..." or "VRULE:num#..." + * + * If a vname is used, the value 0 is set; this is catched + * when graphing. Setting value 0 from the script is not + * permitted + */ + strstart=0; + sscanf(&argv[i][argstart], "%lu#%n" + ,(long unsigned int *)&im.gdes[im.gdes_c-1].xrule + ,&strstart + ); + if (strstart==0) { /* no number, should be vname */ + sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n" + ,varname + ,&strstart + ); + if (strstart!=0) { /* vname matched */ + im.gdes[im.gdes_c-1].xrule = 0;/* signal use of vname */ + if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){ + im_free(&im); + rrd_set_error("unknown variable '%s' in VRULE",varname); + return -1; + } + if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) { + im_free(&im); + rrd_set_error("Only VDEF is allowed in VRULE",varname); + return -1; + } } } else { + if (im.gdes[im.gdes_c-1].xrule==0) + strstart=0; + } + + if (strstart==0) { im_free(&im); rrd_set_error("can't parse '%s'",&argv[i][argstart]); return -1; + } else { + int n=0; + if(sscanf( + &argv[i][argstart+strstart], + "%2x%2x%2x:%n", + &col_red, + &col_green, + &col_blue, + &n)>=3) { + im.gdes[im.gdes_c-1].col.red = col_red; + im.gdes[im.gdes_c-1].col.green = col_green; + im.gdes[im.gdes_c-1].col.blue = col_blue; + if (n==0) { + im.gdes[im.gdes_c-1].legend[0] = '\0'; + } else { + scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend); + } + } else { + im_free(&im); + rrd_set_error("can't parse '%s'",&argv[i][argstart]); + return -1; + } } break; case GF_TICK: @@ -3315,20 +2905,64 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize) rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]); return -1; } - /* checking for duplicate DEF CDEFS */ + /* checking for duplicate variable names */ if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){ im_free(&im); rrd_set_error("duplicate variable '%s'", im.gdes[im.gdes_c-1].vname); return -1; } - if((im.gdes[im.gdes_c-1].rpnp = str2rpn(&im,rpnex))== NULL){ + if((im.gdes[im.gdes_c-1].rpnp = + rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){ rrd_set_error("invalid rpn expression '%s'", rpnex); im_free(&im); return -1; } free(rpnex); break; + case GF_VDEF: + strstart=parse_vname1(&argv[i][argstart],&im,"VDEF"); + if (strstart==0) return -1; + + argstart+=strstart; + sscanf( + &argv[i][argstart], + DEF_NAM_FMT ",%n", + varname,&strstart + ); + if (strstart==0) { + im_free(&im); + rrd_set_error("Cannot parse '%s' in VDEF '%s'", + &argv[i][argstart], + im.gdes[im.gdes_c-1].vname); + return -1; + } + if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname)) == -1) { + im_free(&im); + rrd_set_error("variable '%s' not known in VDEF '%s'", + varname, + im.gdes[im.gdes_c-1].vname); + return -1; + }; + if ( + im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_DEF + && im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_CDEF) { + rrd_set_error("variable '%s' not DEF nor CDEF in VDEF '%s'", + varname, + im.gdes[im.gdes_c-1].vname); + im_free(&im); + return -1; + }; + + /* parsed upto and including the first comma. Now + * see what function is requested. This function + * sets the error string. + */ + if (vdef_parse(&im.gdes[im.gdes_c-1],&argv[i][argstart+strstart])<0) { + im_free(&im); + return -1; + }; + break; case GF_DEF: if (sscanf( &argv[i][argstart], @@ -3435,4 +3069,256 @@ int bad_format(char *fmt) { } return 0; } +int +vdef_parse(gdes,str) +struct graph_desc_t *gdes; +char *str; +{ + /* A VDEF currently is either "func" or "param,func" + * so the parsing is rather simple. Change if needed. + */ + double param; + char func[30]; + int n; + + n=0; + sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n); + if (n==strlen(str)) { /* matched */ + ; + } else { + n=0; + sscanf(str,"%29[A-Z]%n",func,&n); + if (n==strlen(str)) { /* matched */ + param=DNAN; + } else { + rrd_set_error("Unknown function string '%s' in VDEF '%s'" + ,str + ,gdes->vname + ); + return -1; + } + } + if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT; + else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM; + else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE; + else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM; + else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL; + else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST; + else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST; + else { + rrd_set_error("Unknown function '%s' in VDEF '%s'\n" + ,func + ,gdes->vname + ); + return -1; + }; + + switch (gdes->vf.op) { + case VDEF_PERCENT: + if (isnan(param)) { /* no parameter given */ + rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n" + ,func + ,gdes->vname + ); + return -1; + }; + if (param>=0.0 && param<=100.0) { + gdes->vf.param = param; + gdes->vf.val = DNAN; /* undefined */ + gdes->vf.when = 0; /* undefined */ + } else { + rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n" + ,param + ,gdes->vname + ); + return -1; + }; + break; + case VDEF_MAXIMUM: + case VDEF_AVERAGE: + case VDEF_MINIMUM: + case VDEF_TOTAL: + case VDEF_FIRST: + case VDEF_LAST: + if (isnan(param)) { + gdes->vf.param = DNAN; + gdes->vf.val = DNAN; + gdes->vf.when = 0; + } else { + rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n" + ,func + ,gdes->vname + ); + return -1; + }; + break; + }; + return 0; +} +int +vdef_calc(im,gdi) +image_desc_t *im; +int gdi; +{ + graph_desc_t *src,*dst; + rrd_value_t *data; + long step,steps; + + dst = &im->gdes[gdi]; + src = &im->gdes[dst->vidx]; + data = src->data + src->ds + src->ds_cnt; /* skip first value! */ + steps = (src->end - src->start) / src->step; + +#if 0 +printf("DEBUG: start == %lu, end == %lu, %lu steps\n" + ,src->start + ,src->end + ,steps + ); +#endif + + switch (dst->vf.op) { + case VDEF_PERCENT: { + rrd_value_t * array; + int field; + + + if ((array = malloc(steps*sizeof(double)))==NULL) { + rrd_set_error("malloc VDEV_PERCENT"); + return -1; + } + for (step=0;step < steps; step++) { + array[step]=data[step*src->ds_cnt]; + } + qsort(array,step,sizeof(double),vdef_percent_compar); + + field = (steps-1)*dst->vf.param/100; + dst->vf.val = array[field]; + dst->vf.when = 0; /* no time component */ +#if 0 +for(step=0;stepds_cnt])) step++; + if (step == steps) { + dst->vf.val = DNAN; + dst->vf.when = 0; + } else { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + (step+1)*src->step; + } + while (step != steps) { + if (finite(data[step*src->ds_cnt])) { + if (data[step*src->ds_cnt] > dst->vf.val) { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + (step+1)*src->step; + } + } + step++; + } + break; + case VDEF_TOTAL: + case VDEF_AVERAGE: { + int cnt=0; + double sum=0.0; + for (step=0;stepds_cnt])) { + sum += data[step*src->ds_cnt]; + cnt ++; + }; + } + if (cnt) { + if (dst->vf.op == VDEF_TOTAL) { + dst->vf.val = sum*src->step; + dst->vf.when = cnt*src->step; /* not really "when" */ + } else { + dst->vf.val = sum/cnt; + dst->vf.when = 0; /* no time component */ + }; + } else { + dst->vf.val = DNAN; + dst->vf.when = 0; + } + } + break; + case VDEF_MINIMUM: + step=0; + while (step != steps && isnan(data[step*src->ds_cnt])) step++; + if (step == steps) { + dst->vf.val = DNAN; + dst->vf.when = 0; + } else { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + (step+1)*src->step; + } + while (step != steps) { + if (finite(data[step*src->ds_cnt])) { + if (data[step*src->ds_cnt] < dst->vf.val) { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + (step+1)*src->step; + } + } + step++; + } + break; + case VDEF_FIRST: + /* The time value returned here is one step before the + * actual time value. This is the start of the first + * non-NaN interval. + */ + step=0; + while (step != steps && isnan(data[step*src->ds_cnt])) step++; + if (step == steps) { /* all entries were NaN */ + dst->vf.val = DNAN; + dst->vf.when = 0; + } else { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + step*src->step; + } + break; + case VDEF_LAST: + /* The time value returned here is the + * actual time value. This is the end of the last + * non-NaN interval. + */ + step=steps-1; + while (step >= 0 && isnan(data[step*src->ds_cnt])) step--; + if (step < 0) { /* all entries were NaN */ + dst->vf.val = DNAN; + dst->vf.when = 0; + } else { + dst->vf.val = data[step*src->ds_cnt]; + dst->vf.when = src->start + (step+1)*src->step; + } + break; + } + return 0; +} + +/* NaN < -INF < finite_values < INF */ +int +vdef_percent_compar(a,b) +const void *a,*b; +{ + /* Equality is not returned; this doesn't hurt except + * (maybe) for a little performance. + */ + + /* First catch NaN values. They are smallest */ + if (isnan( *(double *)a )) return -1; + if (isnan( *(double *)b )) return 1; + + /* NaN doestn't reach this part so INF and -INF are extremes. + * The sign from isinf() is compatible with the sign we return + */ + if (isinf( *(double *)a )) return isinf( *(double *)a ); + if (isinf( *(double *)b )) return isinf( *(double *)b ); + + /* If we reach this, both values must be finite */ + if ( *(double *)a < *(double *)b ) return -1; else return 1; +}