* rrd__graph.c make creates ne rrds
****************************************************************************/
+#if 0
#include "rrd_tool.h"
+#endif
+
#include <gd.h>
#include <gdlucidan10.h>
#include <gdlucidab12.h>
#include <io.h>
#include <fcntl.h>
#endif
-#include "rrd_rpncalc.h"
+
+#include "rrd_graph.h"
+#include "rrd_graph_helper.h"
#define SmallFont gdLucidaNormal10
#define LargeFont gdLucidaBold12
# 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 if_en {IF_GIF=0,IF_PNG=1};
-
-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"},
/* 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}},
{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 <IMG ... tag and return
- as first retval */
- int lazy; /* only update the gif if there is reasonable
- probablility that the existing one is out of date */
- int logarithmic; /* scale the yaxis logarithmic */
- enum if_en imgformat; /* image format */
-
- /* status information */
-
- long xorigin,yorigin;/* where is (0,0) of the graph */
- long xgif,ygif; /* total size of the gif */
- int interlaced; /* will the graph be interlaced? */
- double magfact; /* numerical magnitude*/
- long base; /* 1000 or 1024 depending on what we graph */
- char symbol; /* magnitude symbol for y-axis */
- int unitsexponent; /* 10*exponent for units on y-asis */
- int extra_flags; /* flags for boolean options */
- /* data elements */
-
- long prt_c; /* number of print elements */
- long gdes_c; /* number of graphics elements */
- graph_desc_t *gdes; /* points to an array of graph elements */
-
-} image_desc_t;
-
-/* Prototypes */
-int xtr(image_desc_t *,time_t);
-int ytr(image_desc_t *, double);
-enum gf_en gf_conv(char *);
-enum if_en if_conv(char *);
-enum tmt_en tmt_conv(char *);
-enum grc_en grc_conv(char *);
-int im_free(image_desc_t *);
-void auto_scale( image_desc_t *, double *, char **, double *);
-void si_unit( image_desc_t *);
-void expand_range(image_desc_t *);
-void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
-int data_fetch( image_desc_t *);
-long find_var(image_desc_t *, char *);
-long find_var_wrapper(void *arg1, char *key);
-long lcd(long *);
-int data_calc( image_desc_t *);
-int data_proc( image_desc_t *);
-time_t find_first_time( time_t, enum tmt_en, long);
-time_t find_next_time( time_t, enum tmt_en, long);
-void gator( gdImagePtr, int, int);
-int print_calc(image_desc_t *, char ***);
-int leg_place(image_desc_t *);
-int horizontal_grid(gdImagePtr, image_desc_t *);
-int horizontal_log_grid(gdImagePtr, image_desc_t *);
-void vertical_grid( gdImagePtr, image_desc_t *);
-void axis_paint( image_desc_t *, gdImagePtr);
-void grid_paint( image_desc_t *, gdImagePtr);
-gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
-int lazy_check(image_desc_t *);
-int graph_paint(image_desc_t *, char ***);
-int gdes_alloc(image_desc_t *);
-int scan_for_col(char *, int, char *);
-int rrd_graph(int, char **, char ***, int *, int *);
-int bad_format(char *);
/* translate time values into x coordinates */
/*#define xtr(x) (int)((double)im->xorigin \
}
}
-
-
/* conversion function for symbolic entry names */
conv_if(TICK,GF_TICK)
conv_if(DEF,GF_DEF)
conv_if(CDEF,GF_CDEF)
+ conv_if(VDEF,GF_VDEF)
return (-1);
}
)
{
- char *symbol[] = {"a", /* 10e-18 Ato */
+ char *symbol[] = {"a", /* 10e-18 Atto */
"f", /* 10e-15 Femto */
"p", /* 10e-12 Pico */
"n", /* 10e-9 Nano */
"k", /* 10e3 Kilo */
"M", /* 10e6 Mega */
"G", /* 10e9 Giga */
- "T", /* 10e12 Terra */
+ "T", /* 10e12 Tera */
"P", /* 10e15 Peta */
"E"};/* 10e18 Exa */
)
{
- char symbol[] = {'a', /* 10e-18 Ato */
+ char symbol[] = {'a', /* 10e-18 Atto */
'f', /* 10e-15 Femto */
'p', /* 10e-12 Pico */
'n', /* 10e-9 Nano */
'k', /* 10e3 Kilo */
'M', /* 10e6 Mega */
'G', /* 10e9 Giga */
- 'T', /* 10e12 Terra */
+ 'T', /* 10e12 Tera */
'P', /* 10e15 Peta */
'E'};/* 10e18 Exa */
long ii;
for(ii=0;ii<im->gdes_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;
return num[i];
}
-/* 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){
long *steparray, rpi;
int stepcnt;
time_t now;
- rpnstack_t rpnstack;
+ rpnstack_t rpnstack;
- rpnstack_init(&rpnstack);
+ rpnstack_init(&rpnstack);
for (gdi=0;gdi<im->gdes_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");
- 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;
+ /* 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;
- /* 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(steparray == NULL){
- rrd_set_error("rpn expressions without variables are not supported");
- rpnstack_free(&rpnstack);
- 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");
- rpnstack_free(&rpnstack);
- return -1;
- }
+ rrd_set_error("malloc im->gdes[gdi].data");
+ rpnstack_free(&rpnstack);
+ 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){
- 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
+ /* Step through the new cdef results array and
+ * calculate the values
*/
- if (rpn_calc(rpnp,&rpnstack,(long) now,
- im->gdes[gdi].data,++dataidx) == -1)
+ for (now = im->gdes[gdi].start;
+ now<=im->gdes[gdi].end;
+ now += im->gdes[gdi].step)
{
- /* rpn_calc sets the error string */
- rpnstack_free(&rpnstack);
- return -1;
- }
-
- } /* enumerate over time steps within a CDEF */
- } /* enumerate over CDEFs */
+ 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;
+ }
+ } /* enumerate over time steps within a CDEF */
+ break;
+ default:
+ continue;
+ }
+ } /* enumerate over CDEFs */
rpnstack_free(&rpnstack);
return 0;
}
case GF_VRULE:
case GF_DEF:
case GF_CDEF:
+ case GF_VDEF:
break;
}
}
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.
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)) {
break;
case GF_DEF:
case GF_CDEF:
+ case GF_VDEF:
break;
}
}
if(data_fetch(im)==-1)
return -1;
- /* evaluate CDEF operations ... */
+ /* evaluate VDEF and CDEF operations ... */
if(data_calc(im)==-1)
return -1;
switch(im->gdes[i].gf){
case GF_CDEF:
+ case GF_VDEF:
case GF_DEF:
case GF_PRINT:
case GF_GPRINT:
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,
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;
}
for(i=optind+1;i<argc;i++){
int argstart=0;
int strstart=0;
- char varname[30],*rpnex;
+ char varname[MAX_VNAME_LEN+1],*rpnex;
gdes_alloc(&im);
if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
case GF_PRINT:
im.prt_c++;
case GF_GPRINT:
- if(sscanf(
- &argv[i][argstart],
- "%29[^#:]:" CF_NAM_FMT ":%n",
- varname,symname,&strstart) == 2){
- scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
- if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
- im_free(&im);
- rrd_set_error("unknown variable '%s'",varname);
- return -1;
- }
- if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
- im_free(&im);
- return -1;
- }
-
- } else {
+ strstart=0;
+ sscanf(&argv[i][argstart], DEF_NAM_FMT ":%n"
+ ,varname
+ ,&strstart
+ );
+
+ if (strstart==0) {
im_free(&im);
- rrd_set_error("can't parse '%s'",&argv[i][argstart]);
+ rrd_set_error("can't parse vname in '%s'",&argv[i][argstart]);
return -1;
- }
+ };
+
+ if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
+ im_free(&im);
+ rrd_set_error("Unknown variable '%s' in (G)PRINT",varname);
+ return -1;
+ } else {
+ int n=0;
+
+ sscanf(&argv[i][argstart+strstart],CF_NAM_FMT ":%n"
+ ,symname
+ ,&n
+ );
+ if (im.gdes[im.gdes[im.gdes_c-1].vidx].gf==GF_VDEF) {
+ /* No consolidation function should be present */
+ if (n != 0) {
+ rrd_set_error("(G)PRINT of VDEF needs no CF");
+ im_free(&im);
+ return -1;
+ }
+ } else {
+ /* A consolidation function should follow */
+ if (n==0) {
+ im_free(&im);
+ rrd_set_error("Missing or incorrect CF in (G)PRINTing '%s' (%s)",varname,&argv[i][argstart]);
+ return -1;
+ };
+ if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
+ im_free(&im);
+ return -1;
+ };
+ strstart+=n;
+ };
+ };
+
+ scan_for_col(
+ &argv[i][argstart+strstart]
+ ,FMT_LEG_LEN
+ ,im.gdes[im.gdes_c-1].format
+ );
break;
case GF_COMMENT:
if(strlen(&argv[i][argstart])>FMT_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:
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'",
}
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("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],
}
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;step<steps;step++)
+printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
+#endif
+ }
+ break;
+ case VDEF_MAXIMUM:
+ 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[steps*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[steps*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;step<steps;step++) {
+ if (finite(data[step*src->ds_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[steps*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[steps*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;
+}