CDEF operators SHIFT, SQRT, SORT, and REV (reverse). See documentation for what
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Tue, 24 Aug 2004 05:26:09 +0000 (05:26 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Tue, 24 Aug 2004 05:26:09 +0000 (05:26 +0000)
they do. This included removal of redundant code in the
rrd_xport path, replaced with a call to rrd_graph_script().
-- David M. Grimes <dgrimes@navisite.com>

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@290 a5681a0c-68f1-0310-ab6d-d61299d08faa

CONTRIBUTORS
doc/rrdgraph_rpn.src
src/rrd_graph.c
src/rrd_graph.h
src/rrd_graph_helper.c
src/rrd_rpncalc.c
src/rrd_rpncalc.h
src/rrd_xport.c

index 9254b18..b924d24 100644 (file)
@@ -60,7 +60,7 @@ Debugging and code contributions
        David L. Barker <dave@ncomtech.com> xport function bug fixes
        Mike Slifcak <slif@bellsouth.net> many rrdtool-1.1.x fixes
        Peter Speck <speck@vitality.dk> eps/svg/pdf file format code in rrdtool-1.x
-
+       David Grimes <dgrimes@navisite.com> SQRT/SORT/REV/SHIFT
 
 Documentation
 
index b765f9a..ae1a91b 100644 (file)
@@ -94,9 +94,9 @@ B<+, -, *, /, %>
 
 Add, subtract, multiply, divide, modulo
 
-B<SIN, COS, LOG, EXP>
+B<SIN, COS, LOG, EXP, SQRT>
 
-Sine, cosine (input in radians), log, exp (natural logarithm)
+Sine, cosine (input in radians), log, exp (natural logarithm), square root
 
 B<ATAN>
 
@@ -108,6 +108,18 @@ Round down,up to the nearest integer
 
 Z<>
 
+=item Ordering Operations
+
+B<SORT, REV>
+
+Pop one element from the stack.  This is the I<count> of items to be sorted
+(or reversed).  The top I<count> of the remaining elements are then sorted
+(or reversed) in place on the stack.
+
+Example: C<CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/> will
+compute the average of the values v1..v6 after removing the smallest and
+largest.
+
 =item Special values
 
 B<UNKN>
index 6d91954..438b3e1 100644 (file)
@@ -188,6 +188,7 @@ enum gf_en gf_conv(char *string){
     conv_if(VDEF,GF_VDEF)
     conv_if(PART,GF_PART)
     conv_if(XPORT,GF_XPORT)
+    conv_if(SHIFT,GF_SHIFT)
     
     return (-1);
 }
@@ -822,6 +823,28 @@ data_calc( image_desc_t *im){
        switch (im->gdes[gdi].gf) {
            case GF_XPORT:
              break;
+           case GF_SHIFT: {
+               graph_desc_t    *vdp = &im->gdes[im->gdes[gdi].vidx];
+               
+               /* remove current shift */
+               vdp->start -= vdp->shift;
+               vdp->end -= vdp->shift;
+               
+               /* vdef */
+               if (im->gdes[gdi].shidx >= 0) 
+                       vdp->shift = im->gdes[im->gdes[gdi].shidx].vf.val;
+               /* constant */
+               else
+                       vdp->shift = im->gdes[gdi].shval;
+
+               /* normalize shift to multiple of consolidated step */
+               vdp->shift = (vdp->shift / vdp->step) * vdp->step;
+
+               /* apply shift */
+               vdp->start += vdp->shift;
+               vdp->end += vdp->shift;
+               break;
+           }
            case GF_VDEF:
                /* A VDEF has no DS.  This also signals other parts
                 * of rrdtool that this is a VDEF value, not a CDEF.
@@ -911,14 +934,13 @@ data_calc( image_desc_t *im){
                 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
                if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
                   im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
-                        long ptr = im->gdes[gdi].rpnp[rpi].ptr;
-                        if(im->gdes[gdi].start > im->gdes[ptr].start) {
-                            im->gdes[gdi].rpnp[rpi].data += im->gdes[gdi].rpnp[rpi].ds_cnt
-                               * ((im->gdes[gdi].start - im->gdes[ptr].start) / im->gdes[ptr].step);
-                        }
+                        long   ptr  = im->gdes[gdi].rpnp[rpi].ptr;
+                       long    diff = im->gdes[gdi].start - im->gdes[ptr].start;
+
+                        if(diff > 0)
+                           im->gdes[gdi].rpnp[rpi].data += (diff / im->gdes[ptr].step) * im->gdes[ptr].ds_cnt;
                      }
                 }
-        
 
                if(steparray == NULL){
                    rrd_set_error("rpn expressions without DEF"
@@ -1016,8 +1038,10 @@ data_proc( image_desc_t *im ){
                        ** the time of the graph. Beware.
                        */
                        vidx = im->gdes[ii].vidx;
-                       if (    ((long int)gr_time >= (long int)im->gdes[vidx].start) &&
-                               ((long int)gr_time <= (long int)im->gdes[vidx].end) ) {
+                       if (im->gdes[vidx].gf == GF_VDEF) {
+                           value = im->gdes[vidx].vf.val;
+                       } else if (((long int)gr_time >= (long int)im->gdes[vidx].start) &&
+                                  ((long int)gr_time <= (long int)im->gdes[vidx].end) ) {
                            value = im->gdes[vidx].data[
                                (unsigned long) floor(
                                    (double)(gr_time - im->gdes[vidx].start)
@@ -1333,6 +1357,7 @@ print_calc(image_desc_t *im, char ***prdata)
        case GF_CDEF:       
        case GF_VDEF:       
        case GF_PART:
+       case GF_SHIFT:
        case GF_XPORT:
            break;
        }
@@ -2348,6 +2373,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
     case GF_HRULE:
     case GF_VRULE:
     case GF_XPORT:
+    case GF_SHIFT:
       break;
     case GF_TICK:
       for (ii = 0; ii < im->xsize; ii++)
@@ -2569,6 +2595,7 @@ gdes_alloc(image_desc_t *im){
     im->gdes[im->gdes_c-1].data_first=0;
     im->gdes[im->gdes_c-1].p_data=NULL;
     im->gdes[im->gdes_c-1].rpnp=NULL;
+    im->gdes[im->gdes_c-1].shift=0;
     im->gdes[im->gdes_c-1].col = 0x0;
     im->gdes[im->gdes_c-1].legend[0]='\0';
     im->gdes[im->gdes_c-1].rrd[0]='\0';
@@ -2631,7 +2658,7 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize, FILE *s
     strncpy(im.graphfile,argv[optind],MAXPATH-1);
     im.graphfile[MAXPATH-1]='\0';
 
-    rrd_graph_script(argc,argv,&im);
+    rrd_graph_script(argc,argv,&im,1);
     if (rrd_test_error()) {
        im_free(&im);
        return -1;
@@ -2776,7 +2803,7 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
            {"lazy",       no_argument,       0,  'z'},
             {"zoom",       required_argument, 0,  'm'},
            {"no-legend",  no_argument,       0,  'g'},
-           {"only-graph", no_argument,       0,  'j'},
+            {"only-graph", no_argument,       0,  'j'},
            {"alt-y-grid", no_argument,       0,  'Y'},
             {"no-minor",   no_argument,       0,  'I'},
            {"alt-autoscale", no_argument,    0,  'A'},
index 13f7160..607ab70 100644 (file)
@@ -27,7 +27,7 @@ enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
 
 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE,
            GF_AREA,GF_STACK,GF_TICK,
-           GF_DEF, GF_CDEF, GF_VDEF,
+           GF_DEF, GF_CDEF, GF_VDEF, GF_SHIFT,
            GF_PART, GF_XPORT};
 
 enum vdef_op_en {
@@ -113,6 +113,11 @@ typedef  struct graph_desc_t {
     vdef_t         vf;         /* instruction for VDEF function */
     rpnp_t         *rpnp;     /* instructions for CDEF function */
 
+    /* SHIFT implementation */
+    int            shidx; /* gdes reference for offset (-1 --> constant) */
+    time_t         shval; /* offset if shidx is -1 */
+    time_t         shift; /* current shift applied */
+
     /* 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 */
@@ -215,7 +220,7 @@ int scan_for_col(char *, int, char *);
 int rrd_graph(int, char **, char ***, int *, int *, FILE *);
 void rrd_graph_init(image_desc_t *);
 void rrd_graph_options(int, char **, image_desc_t *);
-void rrd_graph_script(int, char **, image_desc_t *);
+void rrd_graph_script(int, char **, image_desc_t *, int);
 int rrd_graph_check_vname(image_desc_t *, char *, char *);
 int rrd_graph_color(image_desc_t *, char *, char *, int);
 int rrd_graph_legend(graph_desc_t *, char *);
index a15c0ed..c359f7a 100644 (file)
@@ -15,6 +15,8 @@ int rrd_parse_legend  (char *, unsigned int *, graph_desc_t *);
 int rrd_parse_color   (char *, graph_desc_t *);
 int rrd_parse_CF      (char *, unsigned int *, graph_desc_t *);
 int rrd_parse_print   (char *, unsigned int *, graph_desc_t *, image_desc_t *);
+int rrd_parse_shift   (char *, unsigned int *, graph_desc_t *, image_desc_t *);
+int rrd_parse_xport   (char *, unsigned int *, graph_desc_t *, image_desc_t *);
 int rrd_parse_PVHLAST (char *, unsigned int *, graph_desc_t *, image_desc_t *);
 int rrd_parse_vname   (char *, unsigned int *, graph_desc_t *, image_desc_t *);
 int rrd_parse_def     (char *, unsigned int *, graph_desc_t *, image_desc_t *);
@@ -161,6 +163,77 @@ rrd_parse_print(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t
     return 0;
 }
 
+int
+rrd_parse_shift(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t *im) {
+       char    *l = strdup(line + *eaten), *p;
+       int     rc = 1;
+
+       p = strchr(l, ',');
+       if (p == NULL) {
+               rrd_set_error("Invalid SHIFT syntax");
+               goto out;
+       }
+       *p++ = '\0';
+       
+       if ((gdp->vidx=find_var(im,l))<0) {
+               rrd_set_error("Not a valid vname: %s in line %s",l,line);
+               goto out;
+       }
+       
+        /* constant will parse; otherwise, must be VDEF reference */
+       if (sscanf(p, "%ld", &gdp->shval) != 1) {
+               graph_desc_t    *vdp;
+               
+               if ((gdp->shidx=find_var(im, p))<0) {
+                       rrd_set_error("invalid offset vname: %s", p);
+                       goto out;
+               }
+               
+               vdp = &im->gdes[gdp->shidx];
+               if (vdp->gf != GF_VDEF) {
+                       rrd_set_error("offset must specify value or VDEF");
+                       goto out;
+               }
+       } else {
+               gdp->shidx = -1;
+       }
+       
+       *eaten = strlen(line);
+       rc = 0;
+
+ out:
+       free(l);
+       return rc;
+}
+
+int
+rrd_parse_xport(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t *im) {
+       char    *l = strdup(line + *eaten), *p;
+       int     rc = 1;
+
+       p = strchr(l, ':');
+       if (p != NULL)
+               *p++ = '\0';
+       else
+               p = "";
+       
+       if ((gdp->vidx=find_var(im, l))==-1){
+               rrd_set_error("unknown variable '%s'",l);
+               goto out;
+       }
+       
+       if (strlen(p) >= FMT_LEG_LEN)
+               *(p + FMT_LEG_LEN) = '\0';
+       
+       strcpy(gdp->legend, p);
+       *eaten = strlen(line);
+       rc = 0;
+       
+ out:
+       free(l);
+       return rc;
+}
+
 /* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK
 ** is done in one function.  Stacking STACK is silently ignored
 ** as it is redundant.  Stacking PART, VRULE, HRULE or TICK is
@@ -463,10 +536,10 @@ rrd_parse_cdef(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t
 }
 
 void
-rrd_graph_script(int argc, char *argv[], image_desc_t *im) {
+rrd_graph_script(int argc, char *argv[], image_desc_t *im, int optno) {
     int i;
 
-    for (i=optind+1;i<argc;i++) {
+    for (i=optind+optno;i<argc;i++) {
        graph_desc_t *gdp;
        unsigned int eaten=0;
 
@@ -479,11 +552,11 @@ rrd_graph_script(int argc, char *argv[], image_desc_t *im) {
        if (rrd_parse_find_gf(argv[i],&eaten,gdp)) return;
 
        switch (gdp->gf) {
-#if 0
-       /* future command */
-           case GF_SHIFT:      vname:value
-#endif
+           case GF_SHIFT:      /* vname:value */
+               if (rrd_parse_shift(argv[i],&eaten,gdp,im)) return;
+               break;
            case GF_XPORT:
+               if (rrd_parse_xport(argv[i],&eaten,gdp,im)) return;
                break;
            case GF_PRINT:      /* vname:CF:format -or- vname:format */
            case GF_GPRINT:     /* vname:CF:format -or- vname:format */
index 94796d1..75c24cf 100644 (file)
@@ -154,6 +154,9 @@ void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str)
          add_op(OP_LTIME,LTIME)
          add_op(OP_TIME,TIME)
          add_op(OP_ATAN,ATAN)
+         add_op(OP_SQRT,SQRT)
+         add_op(OP_SORT,SORT)
+         add_op(OP_REV,REV)
 
 #undef add_op
               }
@@ -323,7 +326,9 @@ rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){
        match_op(OP_NOW,NOW)
        match_op(OP_TIME,TIME)
        match_op(OP_ATAN,ATAN)
-
+       match_op(OP_SQRT,SQRT)
+       match_op(OP_SORT,SORT)
+       match_op(OP_REV,REV)
 #undef match_op
 
 
@@ -367,6 +372,14 @@ rpnstack_free(rpnstack_t *rpnstack)
    rpnstack -> dc_stacksize = 0;
 }
 
+static int
+rpn_compare_double(const void *x, const void *y)
+{
+       double  diff = *((const double *)x) - *((const double *)y);
+       
+       return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
+}
+
 /* rpn_calc: run the RPN calculator; also performs variable substitution;
  * moved and modified from data_calc() originally included in rrd_graph.c 
  * arguments:
@@ -660,6 +673,38 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                stackunderflow(0);
                rpnstack->s[stptr] = isinf(rpnstack->s[stptr]) ? 1.0 : 0.0;
                break;
+           case OP_SQRT:
+               stackunderflow(0);
+               rpnstack -> s[stptr] = sqrt(rpnstack -> s[stptr]);
+               break;
+           case OP_SORT:
+               stackunderflow(0);
+               {
+                   int spn = (int)rpnstack -> s[stptr--];
+
+                   stackunderflow(spn-1);
+                   qsort(rpnstack -> s + stptr-spn+1, spn, sizeof(double),
+                         rpn_compare_double);
+               }
+               break;
+           case OP_REV:
+               stackunderflow(0);
+               {
+                   int spn = (int)rpnstack -> s[stptr--];
+                   double *p, *q;
+
+                   stackunderflow(spn-1);
+
+                   p = rpnstack -> s + stptr-spn+1;
+                   q = rpnstack -> s + stptr;
+                   while (p < q) {
+                           double      x = *q;
+                           
+                           *q-- = *p;
+                           *p++ = x;
+                   }
+               }
+               break;
            case OP_END:
                break;
        }
index fe456f2..9fd547a 100644 (file)
@@ -15,7 +15,8 @@ enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
            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,OP_LTIME,OP_NE,OP_ISINF,OP_PREV_OTHER,OP_COUNT,OP_ATAN};
+           OP_UN,OP_END,OP_LTIME,OP_NE,OP_ISINF,OP_PREV_OTHER,OP_COUNT,
+           OP_ATAN,OP_SQRT,OP_SORT,OP_REV};
 
 typedef struct rpnp_t {
     enum op_en   op;
index cceefe2..95bf35b 100644 (file)
@@ -43,11 +43,7 @@ rrd_xport(int argc, char **argv, int *xsize,
 {
 
     image_desc_t   im;
-    int            i;
-    long           long_tmp;
     time_t        start_tmp=0,end_tmp=0;
-    char           symname[100];
-    long           scancount;
     struct rrd_time_value start_tv, end_tv;
     char           *parsetime_error = NULL;
 
@@ -91,12 +87,11 @@ rrd_xport(int argc, char **argv, int *xsize,
            }
            break;
        case 'm':
-           long_tmp = atol(optarg);
-           if (long_tmp < 10) {
+           im.xsize = atol(optarg);
+           if (im.xsize < 10) {
                rrd_set_error("maxrows below 10 rows");
                return -1;
            }
-           im.xsize = long_tmp;
            break;
        case '?':
            rrd_set_error("unknown option '%c'", optopt);
@@ -122,115 +117,10 @@ rrd_xport(int argc, char **argv, int *xsize,
     im.start = start_tmp;
     im.end = end_tmp;
 
-    for(i=optind;i<argc;i++){
-       int   argstart=0;
-       int   strstart=0;
-       char  varname[30],*rpnex;
-       gdes_alloc(&im);
-       if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
-           if((int)(im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
-               im_free(&im);
-               rrd_set_error("unknown function '%s'",symname);
-               return -1;
-           }
-       } else {
-           rrd_set_error("can't parse '%s'",argv[i]);
-           im_free(&im);
-           return -1;
-       }
-
-       switch(im.gdes[im.gdes_c-1].gf){
-       case GF_CDEF:
-           if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
-               rrd_set_error("malloc for CDEF");
-               return -1;
-           }
-           if(sscanf(
-                   &argv[i][argstart],
-                   DEF_NAM_FMT "=%[^: ]",
-                   im.gdes[im.gdes_c-1].vname,
-                   rpnex) != 2){
-               im_free(&im);
-               free(rpnex);
-               rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           /* checking for duplicate DEF CDEFS */
-           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 = rpn_parse(&im,rpnex,&find_var_wrapper))== NULL){
-               rrd_set_error("invalid rpn expression '%s'", rpnex);
-               im_free(&im);           
-               return -1;
-           }
-           free(rpnex);
-           break;
-       case GF_DEF:
-           if (sscanf(
-               &argv[i][argstart],
-               DEF_NAM_FMT "=%n",
-               im.gdes[im.gdes_c-1].vname,
-               &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */ 
-               if(sscanf(&argv[i][argstart
-                                 +strstart
-                                 +scan_for_col(&argv[i][argstart+strstart],
-                                               MAXPATH,im.gdes[im.gdes_c-1].rrd)],
-                         ":" DS_NAM_FMT ":" CF_NAM_FMT,
-                         im.gdes[im.gdes_c-1].ds_nam,
-                         symname) != 2){
-                   im_free(&im);
-                   rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
-                   return -1;
-               }
-           } else {
-               im_free(&im);
-               rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           
-           /* checking for duplicate DEF CDEFS */
-           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((int)(im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
-               im_free(&im);
-               rrd_set_error("unknown cf '%s'",symname);
-               return -1;
-           }
-           break;
-       case GF_XPORT:
-           if((scancount=sscanf(
-               &argv[i][argstart],
-               "%29[^:]:%n",
-               varname,
-               &strstart))>=1){
-               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);
-               }
-               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;
-               }               
-           } else {
-               im_free(&im);
-               rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           break;
-       default:
-         break;
-       }
-       
+    rrd_graph_script(argc,argv,&im,0);
+    if (rrd_test_error()) {
+       im_free(&im);
+       return -1;
     }
 
     if (im.gdes_c == 0){