1 /*
2 * Copyright (c) 2002-2006, Daniel Hartmeier
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <net/if.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <gd.h>
45 #include <gdfonts.h>
46
47 #include "graph.h"
48
49 extern int debug;
50
51 double intersect(double, double, double, double);
52 void find_max(struct matrix *);
53 void normalize(struct matrix *);
54 void draw(gdImagePtr, struct matrix *, struct graph *, int, unsigned);
55 void draw_grid(gdImagePtr, struct matrix *);
56
57 int
graph_add_matrix(struct matrix ** matrices,const char * filename,unsigned type,unsigned width,unsigned height,unsigned beg,unsigned end)58 graph_add_matrix(struct matrix **matrices, const char *filename, unsigned type,
59 unsigned width, unsigned height, unsigned beg, unsigned end)
60 {
61 struct matrix *m;
62 const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
63
64 m = malloc(sizeof(struct matrix));
65 if (m == NULL)
66 err(1, "malloc");
67 m->filename = strdup(filename);
68 if (m->filename == NULL)
69 err(1, "strdup");
70 m->beg = beg;
71 m->end = end;
72 m->type = type;
73 m->width = width;
74 m->height = height;
75 m->w0 = width-fh-18*fw-2*fw;
76 m->h0 = height-5*fh;
77 m->x0 = fh+9*fw;
78 m->y0 = fh;
79 m->graphs[0] = m->graphs[1] = NULL;
80 if (*matrices == NULL)
81 m->next = NULL;
82 else
83 m->next = *matrices;
84 *matrices = m;
85 return (0);
86 }
87
88 int
graph_add_graph(struct graph ** graphs,unsigned width,unsigned desc_nr,const char * label,const char * unit,unsigned color,int filled,int bytes,int type)89 graph_add_graph(struct graph **graphs, unsigned width, unsigned desc_nr,
90 const char *label, const char *unit, unsigned color, int filled,
91 int bytes, int type)
92 {
93 unsigned i;
94 struct graph *g;
95
96 g = malloc(sizeof(struct graph));
97 if (g == NULL)
98 err(1, "malloc");
99 g->unit = strdup(unit);
100 if (g->unit == NULL)
101 err(1, "strdup");
102 g->desc_nr = desc_nr;
103 g->label = strdup(label);
104 if (g->label == NULL)
105 err(1, "strdup");
106 g->color = color;
107 g->filled = filled;
108 g->bytes = bytes;
109 g->type = type;
110 g->data = malloc(width * sizeof(double));
111 if (g->data == NULL)
112 err(1, "graph_add_graph: malloc");
113 for (i = 0; i < width; ++i)
114 g->data[i] = 0.0;
115 g->data_max = 0.0;
116 if (*graphs == NULL)
117 g->next = NULL;
118 else
119 g->next = *graphs;
120 *graphs = g;
121 return (0);
122 }
123
124 int
graph_generate_images(struct matrix * matrices)125 graph_generate_images(struct matrix *matrices)
126 {
127 struct matrix *matrix;
128 const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
129
130 matrix = matrices;
131 while (matrix != NULL) {
132 gdImagePtr im;
133 int white;
134 FILE *out;
135 struct matrix *old_matrix;
136 struct graph *graph, *old_graph;
137 unsigned i, x;
138
139 if (debug > 0)
140 printf("graph_generate_images: generating file %s\n",
141 matrix->filename);
142
143 find_max(matrix);
144 for (i = 0; i < 2; ++i) {
145 double m = 0.0;
146
147 for (graph = matrix->graphs[i]; graph;
148 graph = graph->next)
149 if (graph->data_max > m)
150 m = graph->data_max;
151 for (graph = matrix->graphs[i]; graph;
152 graph = graph->next)
153 graph->data_max = m;
154 if (debug)
155 printf("graph_generate_images: maximum %s "
156 "%.2f\n", i ? "right" : "left", m);
157 }
158 normalize(matrix);
159
160 im = gdImageCreate(matrix->width, matrix->height);
161 white = gdImageColorAllocate(im, 255, 255, 255);
162
163 gdImageFilledRectangle(im, 0, 0, matrix->width-1,
164 matrix->height-1, white);
165
166 draw_grid(im, matrix);
167
168 for (i = 0; i < 2; ++i) {
169 x = i == 0 ? matrix->x0 : matrix->x0+matrix->w0;
170 for (graph = matrix->graphs[i]; graph;
171 graph = graph->next) {
172 int color = gdImageColorAllocate(im,
173 (graph->color >> 16) & 0xFF,
174 (graph->color >> 8) & 0xFF,
175 graph->color & 0xFF);
176
177 draw(im, matrix, graph,
178 color, graph->filled);
179 if (i)
180 x -= strlen(graph->label) * fw;
181 gdImageString(im, gdFontSmall, x,
182 matrix->y0+matrix->h0+5+fh,
183 graph->label, color);
184 if (!i)
185 x += (strlen(graph->label) + 1) * fw;
186 else
187 x -= fw;
188 }
189 }
190
191 out = fopen(matrix->filename, "wb");
192 if (out == NULL) {
193 warn("%s", matrix->filename);
194 return (1);
195 }
196 if (matrix->type == 0)
197 gdImageJpeg(im, out, 95);
198 else
199 gdImagePng(im, out);
200 fclose(out);
201 gdImageDestroy(im);
202
203 /* free matrix */
204 graph = matrix->graphs[0];
205 while (graph != NULL) {
206 old_graph = graph;
207 graph = graph->next;
208 free(old_graph);
209 }
210 graph = matrix->graphs[1];
211 while (graph != NULL) {
212 old_graph = graph;
213 graph = graph->next;
214 free(old_graph);
215 }
216 free(matrix->filename);
217 old_matrix = matrix;
218 matrix = matrix->next;
219 free(old_matrix);
220 }
221
222 return (0);
223 }
224
225 void
find_max(struct matrix * m)226 find_max(struct matrix *m)
227 {
228 unsigned i, j;
229 struct graph *g;
230
231 for (i = 0; i < 2; ++i)
232 for (g = m->graphs[i]; g != NULL; g = g->next) {
233 g->data_max = 0.0;
234 for (j = 0; j < m->w0; ++j)
235 if (g->data[j] > g->data_max)
236 g->data_max = g->data[j];
237 }
238 }
239
240 void
normalize(struct matrix * m)241 normalize(struct matrix *m)
242 {
243 unsigned i, j;
244 struct graph *g;
245
246 for (i = 0; i < 2; ++i)
247 for (g = m->graphs[i]; g != NULL; g = g->next)
248 if (g->data_max != 0.0)
249 for (j = 0; j < m->w0; ++j)
250 g->data[j] /= g->data_max;
251 }
252
253 void
draw(gdImagePtr im,struct matrix * m,struct graph * g,int color,unsigned filled)254 draw(gdImagePtr im, struct matrix *m, struct graph *g, int color,
255 unsigned filled)
256 {
257 unsigned x = m->x0, y = m->y0, w = m->w0, h = m->h0;
258 unsigned dx, dy0 = 0;
259
260 for (dx = 0; dx < w; ++dx) {
261 unsigned dy = g->data[dx] * h;
262
263 if (filled)
264 gdImageLine(im, x+dx+1, y+h-1, x+dx+1, y+h-1-dy,
265 color);
266 else if (dx > 0)
267 gdImageLine(im, x+dx, y+h-1-dy0, x+dx+1, y+h-1-dy,
268 color);
269 dy0 = dy;
270 }
271 }
272
273 void
scale_unit(double * m,char * k,int bytes)274 scale_unit(double *m, char *k, int bytes)
275 {
276 *k = ' ';
277 if (bytes)
278 *m *= 8.0;
279 if (*m >= 1000) {
280 *m /= bytes ? 1024 : 1000;
281 *k = 'k';
282 }
283 if (*m >= 1000) {
284 *m /= bytes ? 1024 : 1000;
285 *k = 'm';
286 }
287 if (*m >= 1000) {
288 *m /= bytes ? 1024 : 1000;
289 *k = 'g';
290 }
291 if (*m >= 1000) {
292 *m /= bytes ? 1024 : 1000;
293 *k = 't';
294 }
295 }
296
297 void
draw_grid(gdImagePtr im,struct matrix * matrix)298 draw_grid(gdImagePtr im, struct matrix *matrix)
299 {
300 const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
301 unsigned x0 = matrix->x0, y0 = matrix->y0,
302 w0 = matrix->w0, h0 = matrix->h0;
303 unsigned len = matrix->end - matrix->beg;
304 double max[2] = { 0.0, 0.0 };
305 const char *unit[2] = { NULL, NULL };
306 unsigned i, ii;
307 char k[2];
308 double dx;
309 char *t;
310 int black = gdImageColorAllocate(im, 0, 0, 0);
311 int grey = gdImageColorAllocate(im, 220, 220, 220);
312
313 if (matrix->graphs[0] != NULL) {
314 max[0] = matrix->graphs[0]->data_max;
315 scale_unit(&max[0], &k[0], matrix->graphs[0]->bytes);
316 unit[0] = matrix->graphs[0]->unit;
317 }
318 if (matrix->graphs[1] != NULL) {
319 max[1] = matrix->graphs[1]->data_max;
320 scale_unit(&max[1], &k[1], matrix->graphs[1]->bytes);
321 unit[1] = matrix->graphs[1]->unit;
322 }
323
324 /* bounding box */
325 gdImageLine(im, x0, y0+h0, x0+w0, y0+h0, black);
326 gdImageLine(im, x0, y0, x0, y0+h0, black);
327 gdImageLine(im, x0+w0, y0, x0+w0, y0+h0, black);
328
329 /* horizontal units */
330 ii = h0 / fh;
331 if (ii > 10)
332 ii = 10;
333 for (i = 0; i <= ii; ++i) {
334 char t[10];
335 unsigned y1 = y0+(i*h0)/ii;
336
337 gdImageLine(im, x0-2, y1, x0, y1, black);
338 gdImageLine(im, x0+w0, y1, x0+w0+2, y1, black);
339 if (i < ii)
340 gdImageLine(im, x0+1, y1, x0+w0-1, y1, grey);
341 if (matrix->graphs[0] != NULL) {
342 snprintf(t, sizeof(t), "%5.1f %c",
343 (max[0]*(ii-i))/ii, k[0]);
344 gdImageString(im, gdFontSmall, fh+fw,
345 y1-fh/2, t, black);
346 }
347 if (matrix->graphs[1] != NULL) {
348 snprintf(t, sizeof(t), "%5.1f %c",
349 (max[1]*(ii-i))/ii, k[1]);
350 gdImageString(im, gdFontSmall, x0+w0+1*fw,
351 y1-fh/2, t, black);
352 }
353 }
354
355 /* time label */
356 t = "minutes";
357 dx = (60.0 * (double)w0) / (double)len; // pixels per unit
358 if (dx < 4*fw) {
359 dx *= 60.0; t = "hours";
360 if (dx < 4*fw) {
361 dx *= 24.0; t = "days";
362 if (dx < 4*fw) {
363 dx *= 7.0; t = "weeks";
364 if (dx < 4*fw) {
365 dx *= 30.0 / 7.0; t = "months";
366 if (dx < 4*fw) {
367 dx *= 365.0 / 30.0;
368 t = "years";
369 }
370 }
371 }
372 }
373 }
374 /* time units */
375 for (i = 0; (i*dx) <= w0; ++i) {
376 unsigned x1 = x0+w0-(i*dx);
377 char t[64];
378
379 gdImageLine(im, x1, y0+h0, x1, y0+h0+2, black);
380 if (x1 > x0 && x1 < x0+w0)
381 gdImageLine(im, x1, y0+h0, x1, y0, grey);
382 if (x1 < x0+w0) {
383 snprintf(t, sizeof(t), "-%u", i);
384 gdImageString(im, gdFontSmall, x1-(strlen(t)*fw)/2,
385 y0+h0+5, t, black);
386 } else {
387 time_t tt = time(0);
388 strncpy(t, asctime(localtime(&tt)), 24); t[24] = 0;
389 gdImageString(im, gdFontSmall, x0+(w0-strlen(t)*fw)/2,
390 y0+h0+5+2*fh, t, black);
391 }
392 }
393 /* hours/days/weeks/months */
394 gdImageString(im, gdFontSmall, x0+w0-strlen(t)*fw, y0+h0+5, t, black);
395 if (matrix->graphs[0] != NULL)
396 gdImageStringUp(im, gdFontSmall, 0,
397 y0+h0-(h0-strlen(unit[0])*fw)/2, (char *)unit[0], black);
398 if (matrix->graphs[1] != NULL)
399 gdImageStringUp(im, gdFontSmall, x0+w0+8*fw,
400 y0+h0-(h0-strlen(unit[1])*fw)/2, (char *)unit[1], black);
401 }
402
403