aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-04-25 00:12:49 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-04-25 00:12:49 +0300
commitb8f6eec86f23d3fadd876654bcc145ef892fd1ea (patch)
tree423408e16973574cc921b59270c6bea67d6bca18
parentc36ee7102c3095f70e8db52aa55046c1393ff4cd (diff)
downloadtagr-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.am1
-rw-r--r--src/graph.c176
-rw-r--r--src/grid.c338
-rw-r--r--src/output.c23
-rw-r--r--src/report.c2
-rw-r--r--src/tagr.h23
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);
}
diff --git a/src/tagr.h b/src/tagr.h
index a88ba9e..944e174 100644
--- a/src/tagr.h
+++ b/src/tagr.h
@@ -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);

Return to:

Send suggestions and report system problems to the System administrator.