diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-04-25 00:12:49 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-04-25 00:12:49 +0300 |
commit | b8f6eec86f23d3fadd876654bcc145ef892fd1ea (patch) | |
tree | 423408e16973574cc921b59270c6bea67d6bca18 | |
parent | c36ee7102c3095f70e8db52aa55046c1393ff4cd (diff) | |
download | tagr-b8f6eec86f23d3fadd876654bcc145ef892fd1ea.tar.gz tagr-b8f6eec86f23d3fadd876654bcc145ef892fd1ea.tar.bz2 |
Finish implementation of draw_graph.
* src/grid.c: New file.
* src/Makefile.am (tagr_SOURCES): Add grid.c
* src/graph.c: Finish implementation of draw_graph.
* src/output.c: Update calls to draw_graph.
* src/report.c (update_router): Use tr->last.time when updating
graphs.
* src/tagr.h (grid_t, struct grid_class): New types.
(grid_create, grid_next, grid_destroy): New protos.
(grid_class_y, grid_class_x_2h, grid_class_x_wday)
(grid_class_x_week, grid_class_x_month): New externs.
(draw_graph): Change prototype.
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/graph.c | 176 | ||||
-rw-r--r-- | src/grid.c | 338 | ||||
-rw-r--r-- | src/output.c | 23 | ||||
-rw-r--r-- | src/report.c | 2 | ||||
-rw-r--r-- | src/tagr.h | 23 |
6 files changed, 526 insertions, 37 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4ef8905..0cc3fc0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ sbin_PROGRAMS=tagr tagr_SOURCES=\ graph.c\ + grid.c\ html.gram.y\ html.lex.l\ log.c\ diff --git a/src/graph.c b/src/graph.c index 1026f35..5933b7f 100644 --- a/src/graph.c +++ b/src/graph.c @@ -53,6 +53,9 @@ int color_percent[3] = { 239,159,79 }; int graph_xsize = 460; int graph_ysize = 100; +int graph_h_margin[2] = { 100, 14 }; +int graph_v_margin[2] = { 14, 35 }; + static char *short_suffix[] = {"", "k", "M", "G", "T"}; char **number_suffix = short_suffix; size_t number_suffix_count = sizeof (short_suffix) / sizeof (short_suffix[0]); @@ -60,29 +63,48 @@ size_t number_suffix_count = sizeof (short_suffix) / sizeof (short_suffix[0]); #define make_color_index(g, ar) \ gdImageColorAllocate (g, (ar)[0], (ar)[1], (ar)[2]) +static void +draw_vtext (gdImagePtr graph, int color, const char *text) +{ + gdImageStringUp (graph, gdFontSmall, + 8, + graph_ysize + graph_v_margin[0] - + (graph_ysize - gdFontSmall->w * strlen (text))/2, + (unsigned char *) text, color); +} + int draw_graph (FILE *fp, queue_t *dataq, const struct avg_acc *avg, time_t now, int xstep, unsigned long xmax, unsigned long ymax, - int growright) + int growright, + struct grid_class *xgrid, struct grid_class *ygrid) { - int x; + int x, y; int i, n; gdImagePtr graph, brush_out, brush_outm, brush_outp; int i_background, i_light, i_dark; int i_major, i_in, i_out, i_grid, i_inm, i_outm; int i_outp, i_outpg; double xscale, yscale; - - yscale = ((double) graph_ysize - 35) / ymax; - xscale = ((double) graph_xsize - 100 - percent_option * 30) / xmax; + int dotted_style[3]; + char *longup; + char *shortup; + int full_xsize = graph_xsize + graph_h_margin[0] + graph_h_margin[1]; + int full_ysize = graph_ysize + graph_v_margin[0] + graph_v_margin[1]; + grid_t grid; + + yscale = (double) graph_ysize / ymax; + xscale = (double) graph_xsize / xmax; #define ytr(y) \ - (unsigned long) ((ymax - (y)) * yscale + 14) + (unsigned long) ((ymax - (y)) * yscale + graph_h_margin[1]) #define xtr(x) \ - (unsigned long) (growright ? ((graph_xsize - (x))) : (x)) + (unsigned long) (growright ? \ + ((full_xsize - (x)*xscale)) : \ + (graph_h_margin[0] + (x)*xscale)) - graph = gdImageCreate (graph_xsize, graph_ysize); + graph = gdImageCreate (full_xsize, full_ysize); brush_out = gdImageCreate (1, 2); brush_outm = gdImageCreate (1, 2); brush_outp = gdImageCreate (1, 2); @@ -106,21 +128,23 @@ draw_graph (FILE *fp, i_outpg = make_color_index (graph, color_percent); /* Draw the image border */ - gdImageLine (graph, 0, 0, graph_xsize - 1, 0, i_light); - gdImageLine (graph, 1, 1, graph_xsize - 2, 1, i_light); - gdImageLine (graph, 0, 0, 0, graph_ysize - 1, i_light); - gdImageLine (graph, 1, 1, 1, graph_ysize - 2, i_light); - gdImageLine (graph, graph_xsize - 1, 0, graph_xsize - 1, - graph_ysize - 1, i_dark); - gdImageLine (graph, 0, graph_ysize - 1, graph_xsize - 1, - graph_ysize - 1, i_dark); - gdImageLine (graph, graph_xsize - 2, 1, graph_xsize - 2, - graph_ysize - 2, i_dark); - gdImageLine (graph, 1, graph_ysize - 2, graph_xsize - 2, - graph_ysize - 2, i_dark); + gdImageLine (graph, 0, 0, full_xsize - 1, 0, i_light); + gdImageLine (graph, 1, 1, full_xsize - 2, 1, i_light); + gdImageLine (graph, 0, 0, 0, full_ysize - 1, i_light); + gdImageLine (graph, 1, 1, 1, full_ysize - 2, i_light); + gdImageLine (graph, full_xsize - 1, 0, full_xsize - 1, + full_ysize - 1, i_dark); + gdImageLine (graph, 0, full_ysize - 1, full_xsize - 1, + full_ysize - 1, i_dark); + gdImageLine (graph, full_xsize - 2, 1, full_xsize - 2, + full_ysize - 2, i_dark); + gdImageLine (graph, 1, full_ysize - 2, full_xsize - 2, + full_ysize - 2, i_dark); n = queue_count (dataq); - for (i = n - 1, x = 0; i > 0; i--, x++) + + /* Incoming traffic */ + for (i = n - 1, x = 0; i > 0 && x < xmax; i--, x += xstep) { struct traffic_history *th = queue_get_ptr (dataq, i); struct traffic_history *tnext = queue_get_ptr (dataq, i - 1); @@ -130,13 +154,113 @@ draw_graph (FILE *fp, xtr (x), ytr (tnext->inrate), i_in); gdImageLine (graph, xtr (x), ytr (th->inrate), xtr (x+1), ytr (tnext->inrate), i_in); -#if 0 - gdImageLine (graph, x + 0.5, ytr (0), - x + 0.5, ytr (th->inrate), i_in); -#endif } - /* FIXME: Drow outgoing traffic, grids and legends */ + /* Outgoing traffic */ + gdImageSetBrush (graph, brush_out); + for (i = n - 1, x = 0; i > 0 && x < xmax; i--, x += xstep) + { + struct traffic_history *th = queue_get_ptr (dataq, i); + struct traffic_history *tnext = queue_get_ptr (dataq, i - 1); + + gdImageLine (graph, xtr (x), ytr (th->outrate), + xtr (x+1), ytr (tnext->outrate), gdBrushed); + } + + /* Border */ + gdImageRectangle (graph, + xtr (0), ytr (0), + xtr (xmax), ytr (ymax), i_grid); + + + dotted_style[0] = i_grid; + dotted_style[1] = gdTransparent; + dotted_style[2] = gdTransparent; + gdImageSetStyle (graph, dotted_style, 3); + + /* draw the horizontal grid */ + /* FIXME: Both should be configurable and I18N */ + longup = "Bits per Second"; + shortup = "Bits/s"; + + grid = grid_create (ygrid, dataq, 0, ymax, NULL); + if (grid) + { + unsigned long i; + char *str = NULL; + + if (graph_ysize < gdFontSmall->w * 16) + draw_vtext (graph, i_grid, shortup); + else + draw_vtext (graph, i_grid, longup); + + /* Draw vertical grid */ + while ((i = grid_next (grid, &str, NULL)) < ymax) + { + int y = ytr (i); + + /* Left tick */ + gdImageLine (graph, + xtr (-2), y, + xtr (1), y, + i_grid); + /* Right tick */ + gdImageLine (graph, + xtr (xmax - 1), y, + xtr (xmax + 1), y, + i_grid); + + /* Grid line */ + gdImageLine (graph, xtr (0), y, xtr (xmax), y, + gdStyled); + + if (str) + gdImageString (graph, gdFontSmall, + 23, y - gdFontSmall->h / 2, + (unsigned char *) str, i_grid); + } + grid_destroy (grid); + } + + grid = grid_create (xgrid, dataq, 0, xmax, &now); + if (grid) + { + unsigned long i; + char *str = NULL; + int mark; + + while ((i = grid_next (grid, &str, &mark)) < xmax) + { + int x = xtr (i); + + /* Tick */ + gdImageLine (graph, + x, ytr (-2), x, ytr (1), + i_grid); + /* Grid line */ + gdImageLine (graph, + x, ytr (0), x, ytr (ymax), + mark ? i_major : gdStyled); + + if (str) + gdImageString (graph, gdFontSmall, + x - strlen (str) * gdFontSmall->w / 2, + graph_ysize + graph_v_margin[0] + 2, + (unsigned char *) str, i_grid); + } + grid_destroy (grid); + } + + /* Mark 0,0 */ + x = graph_h_margin[0]; + y = graph_ysize + graph_h_margin[1]; + gdImageLine (graph, x + 2, y + 3, x + 2, y - 3, i_major); + gdImageLine (graph, x + 1, y + 3, x + 1, y - 3, i_major); + gdImageLine (graph, x, y + 2, x, y - 2, i_major); + gdImageLine (graph, x - 1, y + 1, x - 1, y - 1, i_major); + gdImageLine (graph, x - 2, y + 1, x - 2, y - 1, i_major); + gdImageLine (graph, x - 3, y, x - 3, y, i_major); + gdImagePng (graph, fp); gdImageDestroy (graph); diff --git a/src/grid.c b/src/grid.c new file mode 100644 index 0000000..9028649 --- /dev/null +++ b/src/grid.c @@ -0,0 +1,338 @@ +/* This file is part of tagr. + Copyright (C) 2009 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <unistd.h> +#include <tagr.h> + +struct grid +{ + struct grid_class *class; + queue_t *q; + unsigned long vmin; + unsigned long vmax; + unsigned long cur; + char *str; + void *data; +}; + +grid_t +grid_create (struct grid_class *class, queue_t *q, + unsigned long vmin, unsigned long vmax, void *cdata) +{ + grid_t grid; + + if (!class) + return NULL; + grid = xmalloc (sizeof (*grid)); + grid->class = class; + grid->q = q; + grid->vmin = vmin; + grid->vmax = vmax; + grid->cur = 0; + if (class->create) + grid->data = class->create (grid, cdata); + else + grid->data = NULL; + grid->str = NULL; + return grid; +} + +unsigned long +grid_next (grid_t grid, char **str, int *mark) +{ + return grid->class->next (grid, str, mark); +} + +void +grid_destroy (grid_t grid) +{ + if (grid) + { + if (grid->str) + free (grid->str); + if (grid->class->dealloc) + grid->class->dealloc (grid->data); + free (grid); + } +} + +static void +grid_free_data (void *ptr) +{ + free (ptr); +} + + +static unsigned long +ygrid_next (grid_t grid, char **str, int *mark) +{ + if (mark) + *mark = 0; + grid->cur += 100; /* FIXME: where to configure? */ + if (grid->cur < grid->vmax) + { + if (grid->str) + { + free (grid->str); + grid->str = NULL; + } + asprintf (&grid->str, "%6.1f", (double) grid->cur); + *str = grid->str; + } + return grid->cur; +} + +struct grid_class grid_class_y = { + NULL, + ygrid_next, + NULL +}; + + + +struct xgrid_data +{ + time_t start; + unsigned off; + unsigned unit; +}; + +#define TWOHRS (2*3600) + +static void * +xgrid_2h_create (grid_t grid, void *cdata) +{ + time_t start = *(time_t*)cdata; + struct xgrid_data *gr = xmalloc (sizeof (*gr)); + unsigned long frac = start % 3600; + unsigned long h; + struct tm *tm; + + gr->start = start - frac; + tm = localtime (&gr->start); + h = tm->tm_hour % 2; + gr->off = frac + h * TWOHRS; + gr->unit = tm->tm_hour - h; + + return gr; +} + +static unsigned long +xgrid_2h_next (grid_t grid, char **str, int *mark) +{ + struct xgrid_data *gr = grid->data; + unsigned long ret = gr->off; + + if (grid->str) + { + free (grid->str); + grid->str = NULL; + } + asprintf (&grid->str, "%02d", gr->unit); + *str = grid->str; + if (mark) + *mark = gr->unit == 0; + + gr->off += TWOHRS; + gr->unit = (gr->unit + 24 - 2) % 24; + return ret; +} + +struct grid_class grid_class_x_2h = { + xgrid_2h_create, + xgrid_2h_next, + grid_free_data +}; + + +#define TWENTYFOURHRS 86400 + +static void * +xgrid_wday_create (grid_t grid, void *cdata) +{ + time_t start = *(time_t*)cdata; + struct xgrid_data *gr = xmalloc (sizeof (*gr)); + struct tm *tm = localtime (&start); + unsigned long frac; + + frac = (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec; + gr->start = start - frac; + gr->off = frac; + gr->unit = tm->tm_wday; + + return gr; +} + +static unsigned long +xgrid_wday_next (grid_t grid, char **str, int *mark) +{ + struct xgrid_data *gr = grid->data; + unsigned long ret = gr->off; + char buf[80]; + struct tm *tm = localtime (&gr->start); + + if (grid->str) + { + free (grid->str); + grid->str = NULL; + } + strftime (buf, sizeof buf, "%a", tm); + grid->str = xstrdup (buf); + *str = grid->str; + if (mark) + *mark = tm->tm_wday == 0; + + gr->start -= TWENTYFOURHRS; + gr->off += TWENTYFOURHRS; + return ret; +} + +struct grid_class grid_class_x_wday = { + xgrid_wday_create, + xgrid_wday_next, + grid_free_data +}; + + +#define SEVENDAYS (7*TWENTYFOURHRS) + +static void * +xgrid_week_create (grid_t grid, void *cdata) +{ + time_t start = *(time_t*)cdata; + struct xgrid_data *gr = xmalloc (sizeof (*gr)); + struct tm *tm = localtime (&start); + unsigned long frac; + + frac = ((tm->tm_wday * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + + tm->tm_sec; + gr->start = start - frac; + gr->off = frac; + gr->unit = 0; + + return gr; +} + +static unsigned long +xgrid_week_next (grid_t grid, char **str, int *mark) +{ + struct xgrid_data *gr = grid->data; + unsigned long ret = gr->off; + struct tm *tm = localtime (&gr->start); + + if (tm->tm_mday && tm->tm_mday < 7 && mark && gr->unit == 0) + { + int diff = (tm->tm_mday - 1) * TWENTYFOURHRS; + ret += diff; + *mark = 1; + *str = NULL; + gr->unit = 1; + } + else + { + gr->unit = 0; + if (grid->str) + { + free (grid->str); + grid->str = NULL; + } + asprintf (&grid->str, _("Week %2d"), tm->tm_yday / 7 + 1); + *str = grid->str; + if (mark) + *mark = 0; + + gr->start -= SEVENDAYS; + gr->off += SEVENDAYS; + } + + return ret; +} + +struct grid_class grid_class_x_week = { + xgrid_week_create, + xgrid_week_next, + grid_free_data +}; + + +static void * +xgrid_month_create (grid_t grid, void *cdata) +{ + time_t start = *(time_t*)cdata; + struct xgrid_data *gr = xmalloc (sizeof (*gr)); + struct tm *tm = localtime (&start); + unsigned long frac; + + frac = (((tm->tm_mday - 1) * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + + tm->tm_sec; + gr->start = start - frac; + gr->off = frac; + return gr; +} + +static unsigned long +xgrid_month_next (grid_t grid, char **str, int *mark) +{ + struct xgrid_data *gr = grid->data; + unsigned long ret = gr->off; + unsigned long frac; + struct tm *tm = localtime (&gr->start); + int mon; + char buf[80]; + + if (grid->str) + { + free (grid->str); + grid->str = NULL; + } + strftime (buf, sizeof buf, "%b", tm); + grid->str = xstrdup (buf); + *str = grid->str; + if (mark) + *mark = tm->tm_mon == 0; + + if (tm->tm_mon == 0) + mon = 11; + else + mon = tm->tm_mon - 1; + + do + { + gr->start -= TWENTYFOURHRS; + gr->off += TWENTYFOURHRS; + tm = localtime (&gr->start); + } + while (!(mon == tm->tm_mon && tm->tm_mday == 1)); + + frac = (((tm->tm_mday - 1) * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + + tm->tm_sec; + gr->start -= frac; + gr->off += frac; + + return ret; +} + +struct grid_class grid_class_x_month = { + xgrid_month_create, + xgrid_month_next, + grid_free_data +}; diff --git a/src/output.c b/src/output.c index b205a4e..ae2cc60 100644 --- a/src/output.c +++ b/src/output.c @@ -68,14 +68,14 @@ create_hierarchy (char *dir, int perm) static int do_update_output (SD *host, queue_t *queue, const struct avg_acc *avg, - time_t timestamp, - size_t step, size_t count, + size_t step, time_t timestamp, + struct grid_class *xgrid, struct grid_class *ygrid, char *dirname, char *img_suffix) { char *fname; FILE *fp; int rc; - + if (create_hierarchy (dirname, 0755)) { logmsg (L_ERR, _("cannot create directory %s"), @@ -92,8 +92,9 @@ do_update_output (SD *host, } else { - rc = draw_graph (fp, queue, avg, timestamp, step, count, - host->max, 0); + rc = draw_graph (fp, queue, avg, timestamp, step, + step*460 /* FIXME: must be queue->size * step */, + host->max/8, 0, xgrid, ygrid); fclose (fp); } free (fname); @@ -107,16 +108,20 @@ update_output (SD *host, struct traffic_record *tr, time_t timestamp) int rc; rc = do_update_output (host, &tr->day_hist, NULL, - DAY_SAMPLE, DAY_COUNT, timestamp, + DAY_SAMPLE, timestamp, + &grid_class_x_2h, &grid_class_y, dirname, "-day.png"); rc += do_update_output (host, &tr->week_hist, &tr->week_avg, - WEEK_SAMPLE, WEEK_COUNT, timestamp, + WEEK_SAMPLE, timestamp, + &grid_class_x_wday, &grid_class_y, dirname, "-week.png"); rc += do_update_output (host, &tr->month_hist, &tr->month_avg, - MONTH_SAMPLE, MONTH_COUNT, timestamp, + MONTH_SAMPLE, timestamp, + &grid_class_x_week, &grid_class_y, dirname, "-month.png"); rc += do_update_output (host, &tr->year_hist, &tr->year_avg, - YEAR_SAMPLE, YEAR_COUNT, timestamp, + YEAR_SAMPLE, timestamp, + &grid_class_x_month, &grid_class_y, dirname, "-year.png"); free (dirname); return rc; diff --git a/src/report.c b/src/report.c index 3a7fc1c..ea67ab8 100644 --- a/src/report.c +++ b/src/report.c @@ -257,7 +257,7 @@ update_router (datum key, time_t timestamp) _read_db (key, &tr); if (tr) { - update_output (sd, tr, timestamp); + update_output (sd, tr, tr->last.time);/*FIXME: must be now*/ free (tr); } @@ -218,7 +218,28 @@ int update_output (SD *host, struct traffic_record *tr, time_t timestamp); /* graph.c */ +typedef struct grid *grid_t; + +struct grid_class +{ + void *(*create) (grid_t, void *); + unsigned long (*next) (grid_t, char **, int *); + void (*dealloc) (void *); +}; + +grid_t grid_create (struct grid_class *, queue_t *, unsigned long, + unsigned long, void *); +unsigned long grid_next (grid_t, char **, int *); +void grid_destroy (grid_t); + +extern struct grid_class grid_class_y; +extern struct grid_class grid_class_x_2h; +extern struct grid_class grid_class_x_wday; +extern struct grid_class grid_class_x_week; +extern struct grid_class grid_class_x_month; + int draw_graph (FILE *fp, queue_t *dataq, const struct avg_acc *avg, time_t now, int xstep, unsigned long xmax, unsigned long ymax, - int growright); + int growright, + struct grid_class *xgrid, struct grid_class *ygrid); |