A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 #include "common.h" 2 #include "dbsql.h" 3 #include "misc.h" 4 #include "image.h" 5 #include "image_support.h" 6 #include "vnstati.h" 7 8 void initimagecontent(IMAGECONTENT *ic) 9 { 10 ic->im = NULL; 11 ic->font = gdFontGetSmall(); 12 ic->lineheight = 12; 13 ic->large = 0; 14 ic->showheader = 1; 15 ic->showedge = 1; 16 ic->showlegend = 1; 17 ic->altdate = 0; 18 ic->headertext[0] = '\0'; 19 ic->databegin[0] = '\0'; 20 ic->dataend[0] = '\0'; 21 ic->interface.name[0] = '\0'; 22 ic->interface.alias[0] = '\0'; 23 } 24 25 void drawimage(IMAGECONTENT *ic) 26 { 27 switch (cfg.qmode) { 28 case 1: 29 drawlist(ic, "day"); 30 break; 31 case 2: 32 drawlist(ic, "month"); 33 break; 34 case 3: 35 drawlist(ic, "top"); 36 break; 37 case 4: 38 drawlist(ic, "year"); 39 break; 40 case 5: 41 drawsummary(ic, 0, 0); 42 break; 43 case 51: 44 drawsummary(ic, 1, cfg.hourlyrate); // horizontal 45 break; 46 case 52: 47 drawsummary(ic, 2, cfg.hourlyrate); // vertical 48 break; 49 case 7: 50 drawhourly(ic, cfg.hourlyrate); 51 break; 52 case 8: 53 drawlist(ic, "hour"); 54 break; 55 case 9: 56 drawlist(ic, "fiveminute"); 57 break; 58 case 10: 59 drawfivegraph(ic, cfg.hourlyrate, cfg.fivegresultcount, cfg.fivegheight); 60 break; 61 default: 62 printf("Error: No such query mode: %d\n", cfg.qmode); 63 exit(EXIT_FAILURE); 64 } 65 66 /* enable background transparency if needed */ 67 if (cfg.transbg) { 68 gdImageColorTransparent(ic->im, ic->cbackground); 69 } 70 } 71 72 #if HAVE_DECL_GD_NEAREST_NEIGHBOUR 73 void scaleimage(IMAGECONTENT *ic) 74 { 75 gdImagePtr im_scaled; 76 unsigned int width = 0, height = 0; 77 78 if (cfg.imagescale == 100 || ic->im == NULL) { 79 return; 80 } 81 82 width = (unsigned int)((float)gdImageSX(ic->im) * ((float)cfg.imagescale / (float)100)); 83 height = (unsigned int)((float)gdImageSY(ic->im) * ((float)cfg.imagescale / (float)100)); 84 85 if (width < 100 || height < 100) { 86 return; 87 } 88 89 if (width > 5000 || height > 5000) { 90 return; 91 } 92 93 /* keep output sharp when percent is an exact multiplier */ 94 if (cfg.imagescale % 100 == 0) { 95 gdImageSetInterpolationMethod(ic->im, GD_NEAREST_NEIGHBOUR); 96 } 97 98 im_scaled = gdImageScale(ic->im, width, height); 99 if (im_scaled == NULL) { 100 return; 101 } 102 103 gdImageDestroy(ic->im); 104 ic->im = im_scaled; 105 } 106 #endif 107 108 int drawhours(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate) 109 { 110 int i, tmax = 0, s = 0, step, prev = 0, diff = 0, chour; 111 int x = xpos, y = ypos, extrax = 0, extray = 0, xt = 0; 112 double ratediv; 113 uint64_t max = 1, scaleunit = 0; 114 char buffer[32]; 115 struct tm *d; 116 dbdatalist *datalist = NULL, *datalist_i = NULL; 117 dbdatalistinfo datainfo; 118 HOURDATA hourdata[24]; 119 gdFontPtr font; 120 121 if (ic->large) { 122 font = gdFontGetSmall(); 123 } else { 124 font = gdFontGetTiny(); 125 } 126 127 for (i = 0; i < 24; i++) { 128 hourdata[i].rx = hourdata[i].tx = 0; 129 hourdata[i].date = 0; 130 } 131 132 if (cfg.hourlygmode == 0) { 133 buffer[0] = '\0'; 134 } else { 135 snprintf(buffer, 32, "today"); 136 } 137 138 if (!db_getdata_range(&datalist, &datainfo, ic->interface.name, "hour", 24, buffer, "") || datainfo.count == 0) { 139 gdImageString(ic->im, ic->font, x + (32 * ic->font->w), y + 54, (unsigned char *)"no data available", ic->ctext); 140 return 0; 141 } 142 143 datalist_i = datalist; 144 145 while (datalist_i != NULL) { 146 d = localtime(&datalist_i->timestamp); 147 if (hourdata[d->tm_hour].date != 0 || ic->interface.updated - datalist_i->timestamp > 86400) { 148 datalist_i = datalist_i->next; 149 continue; 150 } 151 hourdata[d->tm_hour].rx = datalist_i->rx; 152 hourdata[d->tm_hour].tx = datalist_i->tx; 153 hourdata[d->tm_hour].date = datalist_i->timestamp; 154 datalist_i = datalist_i->next; 155 } 156 dbdatalistfree(&datalist); 157 158 ic->current = ic->interface.updated; 159 chour = localtime(&ic->current)->tm_hour; 160 161 if (cfg.rateunit) { 162 ratediv = 450; /* x * 8 / 3600 */ 163 } else { 164 ratediv = 3600; 165 } 166 167 /* tmax (time max) = current hour */ 168 /* max = transfer max */ 169 170 for (i = 0; i < 24; i++) { 171 /* convert hourly transfer to hourly rate if needed */ 172 if (rate) { 173 if ((ic->current - hourdata[i].date) > 3600) { 174 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv); 175 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv); 176 } else { 177 /* scale ongoing hour properly */ 178 if (chour != i) { 179 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv); 180 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv); 181 } else { 182 d = localtime(&ic->current); 183 diff = d->tm_min * 60; 184 if (!diff) { 185 diff = 60; 186 } 187 if (cfg.rateunit == 1) { 188 hourdata[i].rx *= 8; 189 hourdata[i].tx *= 8; 190 } 191 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / (double)diff); 192 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / (double)diff); 193 } 194 } 195 } 196 197 if (hourdata[i].date >= hourdata[tmax].date) { 198 tmax = i; 199 } 200 if (hourdata[i].rx >= max) { 201 max = hourdata[i].rx; 202 } 203 if (hourdata[i].tx >= max) { 204 max = hourdata[i].tx; 205 } 206 } 207 208 if (ic->large) { 209 x += 14; 210 extrax = 145; 211 extray = 35; 212 } 213 214 /* scale values */ 215 scaleunit = getscale(max, rate); 216 217 s = (int)lrint(((double)scaleunit / (double)max) * (124 + extray)); 218 if (s < SCALEMINPIXELS) { 219 step = 2; 220 } else { 221 step = 1; 222 } 223 224 xt = x + 36; 225 226 for (i = step; i * s <= (124 + extray + 4); i = i + step) { 227 gdImageDashedLine(ic->im, xt, y + 124 - (i * s), xt + 424 + extrax, y + 124 - (i * s), ic->cline); 228 gdImageDashedLine(ic->im, xt, y + 124 - prev - (step * s) / 2, xt + 424 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel); 229 gdImageString(ic->im, font, x + 16 - (ic->large * 3), y + 121 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext); 230 prev = i * s; 231 } 232 if ((prev + (step * s) / 2) <= (124 + extray + 4)) { 233 gdImageDashedLine(ic->im, xt, y + 124 - prev - (step * s) / 2, xt + 424 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel); 234 } 235 236 /* scale text */ 237 gdImageStringUp(ic->im, font, x - 2 - (ic->large * 14), y + 58 + (rate * 10) - (extray / 2), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext); 238 239 /* axis */ 240 gdImageLine(ic->im, xt - 4, y + 124, xt + 430 + extrax, y + 124, ic->ctext); 241 gdImageLine(ic->im, xt, y - 10 - extray, xt, y + 124 + 4, ic->ctext); 242 243 /* arrows */ 244 drawarrowup(ic, xt, y - 9 - extray); 245 drawarrowright(ic, xt + 429 + extrax, y + 124); 246 247 xt = x + 440 + extrax; 248 249 /* keep alignment when midnight line isn't shown s*/ 250 if (cfg.hourlygmode || tmax - 23 == 0) { 251 xt--; 252 } 253 254 /* x-axis values and poles */ 255 for (i = 0; i < 24; i++) { 256 if (cfg.hourlygmode == 0) { 257 s = tmax - i; 258 if (s < 0) { 259 s += 24; 260 } 261 } else { 262 s = 23 - i; 263 } 264 snprintf(buffer, 32, "%02d ", s); 265 if (hourdata[s].date == 0) { 266 chour = ic->cline; 267 } else { 268 chour = ic->ctext; 269 } 270 gdImageString(ic->im, font, xt, y + 128, (unsigned char *)buffer, chour); 271 drawpoles(ic, xt - 2, y - extray, 124 + extray, hourdata[s].rx, hourdata[s].tx, max); 272 gdImageLine(ic->im, xt - 4 - (ic->large * 3), y + 124, xt + 12 + (ic->large * 3), y + 124, chour); 273 if (s == 0 && i != 23) { 274 /* midnight line */ 275 gdImageLine(ic->im, xt - 5 - (ic->large * 3), y - 5 - extray, xt - 5 - (ic->large * 3), y + 124 - 1, ic->clinel); 276 xt--; 277 } 278 xt = xt - (17 + ic->large * 6); 279 } 280 281 return 1; 282 } 283 284 void drawhourly(IMAGECONTENT *ic, const int rate) 285 { 286 int width, height, headermod = 0; 287 288 width = 500 + (ic->large * 168); 289 height = 200 + (ic->large * 48); 290 291 if (!ic->showheader) { 292 headermod = 26; 293 height -= 22; 294 } 295 296 imageinit(ic, width, height); 297 layoutinit(ic, " / hourly", width, height); 298 299 if (drawhours(ic, 12, 46 - headermod + (ic->large * 40), rate)) { 300 drawlegend(ic, width / 2 - (ic->large * 10), 183 - headermod + (ic->large * 46), 0); 301 } 302 } 303 304 void drawlist(IMAGECONTENT *ic, const char *listname) 305 { 306 ListType listtype = LT_None; 307 int textx, texty, offsetx = 0; 308 int width, height, headermod, i = 1, rowcount = 0; 309 int estimateavailable = 0, estimatevisible = 0; 310 int32_t limit; 311 uint64_t e_rx = 0, e_tx = 0, e_secs = 0; 312 char buffer[512], datebuff[16], daybuff[16]; 313 char stampformat[64], titlename[16], colname[8]; 314 struct tm *d; 315 time_t current; 316 dbdatalist *datalist = NULL, *datalist_i = NULL; 317 dbdatalistinfo datainfo; 318 319 if (strcmp(listname, "day") == 0) { 320 listtype = LT_Day; 321 strncpy_nt(colname, listname, 8); 322 snprintf(titlename, 16, "daily"); 323 strncpy_nt(stampformat, cfg.dformat, 64); 324 limit = cfg.listdays; 325 } else if (strcmp(listname, "month") == 0) { 326 listtype = LT_Month; 327 strncpy_nt(colname, listname, 8); 328 snprintf(titlename, 16, "monthly"); 329 strncpy_nt(stampformat, cfg.mformat, 64); 330 limit = cfg.listmonths; 331 } else if (strcmp(listname, "year") == 0) { 332 listtype = LT_Year; 333 strncpy_nt(colname, listname, 8); 334 snprintf(titlename, 16, "yearly"); 335 strncpy_nt(stampformat, "%Y", 64); 336 limit = cfg.listyears; 337 } else if (strcmp(listname, "top") == 0) { 338 listtype = LT_Top; 339 snprintf(colname, 8, "day"); 340 strncpy_nt(stampformat, cfg.tformat, 64); 341 limit = cfg.listtop; 342 offsetx = 5 * ic->font->w; 343 } else if (strcmp(listname, "hour") == 0) { 344 listtype = LT_Hour; 345 strncpy_nt(colname, listname, 8); 346 snprintf(titlename, 16, "hourly"); 347 strncpy_nt(stampformat, "%H:%M", 64); 348 limit = cfg.listhours; 349 } else if (strcmp(listname, "fiveminute") == 0) { 350 listtype = LT_5min; 351 strncpy_nt(colname, "time", 8); 352 snprintf(titlename, 16, "5 minute"); 353 strncpy_nt(stampformat, "%H:%M", 64); 354 limit = cfg.listfivemins; 355 } else { 356 return; 357 } 358 359 if (limit < 0) { 360 limit = 0; 361 } 362 363 daybuff[0] = '\0'; 364 365 db_getdata_range(&datalist, &datainfo, ic->interface.name, listname, (uint32_t)limit, ic->databegin, ic->dataend); 366 367 datalist_i = datalist; 368 369 if (strlen(ic->dataend) == 0 && datainfo.count > 0 && listtype != LT_Top) { 370 getestimates(&e_rx, &e_tx, listtype, ic->interface.updated, &datalist); 371 if ((cfg.estimatestyle > 0 || cfg.barshowsrate > 0) && e_rx + e_tx > datainfo.max) { 372 datainfo.max = e_rx + e_tx; 373 } 374 estimateavailable = 1; 375 if (listtype == LT_Day || listtype == LT_Month || listtype == LT_Year) { 376 estimatevisible = 1; 377 } 378 } 379 380 if (listtype == LT_Top) { 381 if (limit > 0 && datainfo.count < (uint32_t)limit) { 382 limit = (int32_t)datainfo.count; 383 } 384 if (limit <= 0 || datainfo.count > 999) { 385 snprintf(titlename, 16, "top"); 386 } else { 387 snprintf(titlename, 16, "top %d", limit); 388 } 389 } 390 391 if (listtype == LT_Hour || listtype == LT_5min) { 392 while (datalist_i != NULL) { 393 d = localtime(&datalist_i->timestamp); 394 strftime(datebuff, 16, cfg.dformat, d); 395 if (strcmp(daybuff, datebuff) != 0) { 396 rowcount += 1; 397 strcpy(daybuff, datebuff); 398 } 399 datalist_i = datalist_i->next; 400 } 401 datalist_i = datalist; 402 daybuff[0] = '\0'; 403 } 404 rowcount += datainfo.count; 405 406 width = 83 * ic->font->w + 2 + (ic->large * 2); 407 height = 62 + 3 * ic->lineheight; 408 409 // less space needed when no estimate or sum is shown (Top, 5min and Hour never have estimate) 410 if ((!estimatevisible && datainfo.count < 2) || (listtype == LT_Top || listtype == LT_Hour || listtype == LT_5min)) { 411 height = 62 + 2 * ic->lineheight; 412 } 413 414 // exception for 5min and Hour when having sum shown 415 if ((listtype == LT_5min || listtype == LT_Hour) && datainfo.count > 1 && strlen(ic->dataend) > 0) { 416 height = 62 + 3 * ic->lineheight; 417 } 418 419 height += (ic->lineheight + cfg.linespaceadjust) * rowcount - cfg.linespaceadjust; 420 421 // "no data available" 422 if (!datainfo.count) { 423 height = 98 + (ic->large * 12); 424 } 425 426 if (!ic->showheader) { 427 headermod = 26; 428 height -= 22; 429 } else { 430 headermod = 0; 431 } 432 433 snprintf(buffer, 512, " / %s", titlename); 434 435 imageinit(ic, width, height); 436 layoutinit(ic, buffer, width, height); 437 438 if (datainfo.count) { 439 if (listtype == LT_Top) { 440 if (cfg.ostyle <= 2) { 441 drawlegend(ic, 66 * ic->font->w + 2, 40 - headermod, 0); 442 } 443 current = time(NULL); 444 d = localtime(¤t); 445 strftime(daybuff, 16, stampformat, d); 446 } else { // everything else 447 if (cfg.ostyle > 2) { 448 if (estimateavailable && cfg.barshowsrate) { 449 drawlegend(ic, 72 * ic->font->w, 40 - headermod, 1); 450 } else { 451 drawlegend(ic, 72 * ic->font->w, 40 - headermod, 0); 452 } 453 } else { 454 drawlegend(ic, 64 * ic->font->w + 1, 40 - headermod, 0); 455 } 456 } 457 } 458 459 textx = 10; 460 texty = 40 - headermod; 461 462 if (listtype == LT_Top) { // top 463 snprintf(buffer, 512, " # day rx tx total"); 464 } else { // everything else 465 snprintf(buffer, 512, " %8s rx tx total", colname); 466 } 467 if (cfg.ostyle > 2) { 468 strcat(buffer, " avg. rate"); 469 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext); 470 gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (65 * ic->font->w) + offsetx + 2, texty + ic->lineheight + 4, ic->cline); 471 } else { 472 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext); 473 gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (50 * ic->font->w) + offsetx - 4, texty + ic->lineheight + 4, ic->cline); 474 } 475 476 texty += ic->lineheight + 8; 477 478 if (datainfo.count) { 479 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline); 480 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline); 481 if (cfg.ostyle > 2) { 482 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline); 483 } 484 } else { 485 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty - 4, ic->cline); 486 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty - 4, ic->cline); 487 if (cfg.ostyle > 2) { 488 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty - 4, ic->cline); 489 } 490 } 491 492 while (datalist_i != NULL) { 493 d = localtime(&datalist_i->timestamp); 494 495 if (listtype == LT_5min || listtype == LT_Hour) { 496 strftime(datebuff, 16, cfg.dformat, d); 497 if (strcmp(daybuff, datebuff) != 0) { 498 snprintf(buffer, 32, " %s", datebuff); 499 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext); 500 texty += ic->lineheight + cfg.linespaceadjust; 501 strcpy(daybuff, datebuff); 502 } 503 } 504 505 if (listtype == LT_Top) { 506 if (strftime(datebuff, 16, stampformat, d) <= 8) { 507 snprintf(buffer, 32, " %2d %*s", i, getpadding(8, datebuff), datebuff); 508 strcat(buffer, " "); 509 } else { 510 snprintf(buffer, 32, " %2d %-*s ", i, getpadding(11, datebuff), datebuff); 511 } 512 if (strcmp(datebuff, daybuff) == 0) { 513 if (cfg.ostyle > 2) { 514 gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (65 * ic->font->w) + offsetx + 2, texty + 11 + (ic->large * 3), ic->cbgoffset); 515 } else { 516 gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (50 * ic->font->w) + offsetx - 4, texty + 11 + (ic->large * 3), ic->cbgoffset); 517 } 518 } 519 } else { 520 if (strftime(datebuff, 16, stampformat, d) <= 8) { 521 snprintf(buffer, 32, " %*s", getpadding(8, datebuff), datebuff); 522 strcat(buffer, " "); 523 } else { 524 snprintf(buffer, 32, " %-*s ", getpadding(11, datebuff), datebuff); 525 } 526 } 527 strncat(buffer, getvalue(datalist_i->rx, 10, RT_Normal), 32); 528 strcat(buffer, " "); 529 strncat(buffer, getvalue(datalist_i->tx, 10, RT_Normal), 32); 530 strcat(buffer, " "); 531 strncat(buffer, getvalue(datalist_i->rx + datalist_i->tx, 10, RT_Normal), 32); 532 if (cfg.ostyle > 2) { 533 strcat(buffer, " "); 534 if (datalist_i->next == NULL && issametimeslot(listtype, datalist_i->timestamp, ic->interface.updated)) { 535 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 1); 536 } else { 537 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 0); 538 } 539 strncat(buffer, gettrafficrate(datalist_i->rx + datalist_i->tx, (time_t)e_secs, 14), 32); 540 } 541 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext); 542 if (listtype == LT_Top) { 543 if (cfg.ostyle > 2) { 544 drawbar(ic, textx + (71 * ic->font->w) + 2, texty + 4, 9 * ic->font->w - 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 545 } else { 546 drawbar(ic, textx + (56 * ic->font->w), texty + 4, 23 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 547 } 548 } else { // everything else 549 if (cfg.ostyle > 2) { 550 if (datalist_i->next == NULL && estimateavailable && cfg.barshowsrate) { 551 drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 0); 552 } else { 553 drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 554 } 555 } else { 556 drawbar(ic, textx + (51 * ic->font->w) - 2, texty + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 557 } 558 } 559 texty += ic->lineheight + cfg.linespaceadjust; 560 if (datalist_i->next == NULL) { 561 texty -= cfg.linespaceadjust; 562 break; 563 } 564 datalist_i = datalist_i->next; 565 i++; 566 } 567 568 if (!datainfo.count) { 569 i = 17 * ic->font->w; 570 if (cfg.ostyle > 2) { 571 i += 8 * ic->font->w; 572 } 573 gdImageString(ic->im, ic->font, textx + i, texty, (unsigned char *)"no data available", ic->ctext); 574 texty += ic->lineheight; 575 } 576 577 if (cfg.ostyle > 2) { 578 gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (65 * ic->font->w) + offsetx + 2, texty + 5 - (ic->large * 2), ic->cline); 579 } else { 580 gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (50 * ic->font->w) + offsetx - 4, texty + 5 - (ic->large * 2), ic->cline); 581 } 582 583 buffer[0] = '\0'; 584 585 /* estimate visible */ 586 if (estimatevisible) { 587 snprintf(buffer, 32, " estimated "); 588 strncat(buffer, getvalue(e_rx, 10, RT_Estimate), 32); 589 strcat(buffer, " "); 590 strncat(buffer, getvalue(e_tx, 10, RT_Estimate), 32); 591 strcat(buffer, " "); 592 strncat(buffer, getvalue(e_rx + e_tx, 10, RT_Estimate), 32); 593 594 if (cfg.estimatestyle) { 595 if (cfg.ostyle > 2) { 596 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 1); 597 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 598 } else { 599 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, e_rx, e_tx, datainfo.max, 1); 600 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0); 601 } 602 } 603 604 /* sum visible */ 605 } else if (strlen(ic->dataend) > 0 && datainfo.count > 1 && listtype != LT_Top) { 606 if (datainfo.count < 100) { 607 snprintf(datebuff, 16, "sum of %" PRIu32 "", datainfo.count); 608 } else { 609 snprintf(datebuff, 16, "sum"); 610 } 611 snprintf(buffer, 32, " %9s ", datebuff); 612 strncat(buffer, getvalue(datainfo.sumrx, 10, RT_Normal), 32); 613 strcat(buffer, " "); 614 strncat(buffer, getvalue(datainfo.sumtx, 10, RT_Normal), 32); 615 strcat(buffer, " "); 616 strncat(buffer, getvalue(datainfo.sumrx + datainfo.sumtx, 10, RT_Normal), 32); 617 } 618 619 if (strlen(buffer) > 0) { 620 texty += 8; 621 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext); 622 623 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6, textx + (24 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline); 624 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6, textx + (37 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline); 625 if (cfg.ostyle > 2) { 626 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6, textx + (50 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline); 627 } 628 } 629 630 dbdatalistfree(&datalist); 631 } 632 633 void drawsummary(IMAGECONTENT *ic, const int layout, const int rate) 634 { 635 int width, height, headermod; 636 637 switch (layout) { 638 // horizontal 639 case 1: 640 width = 163 * ic->font->w + 2 + (ic->large * 2); 641 height = 56 + 12 * ic->lineheight; 642 break; 643 // vertical 644 case 2: 645 width = 83 * ic->font->w + 2 + (ic->large * 2); 646 height = 370 + (ic->large * 90); 647 break; 648 // no hours 649 default: 650 width = 83 * ic->font->w + 2 + (ic->large * 2); 651 height = 56 + 12 * ic->lineheight; 652 break; 653 } 654 655 if (!ic->showheader) { 656 headermod = 26; 657 height -= 22; 658 } else { 659 headermod = 0; 660 } 661 662 imageinit(ic, width, height); 663 layoutinit(ic, "", width, height); 664 665 if (ic->interface.rxtotal == 0 && ic->interface.txtotal == 0) { 666 gdImageString(ic->im, ic->font, 33 * ic->font->w, 100, (unsigned char *)"no data available", ic->ctext); 667 return; 668 } 669 670 drawsummary_alltime(ic, 385 + (ic->large * 125), 57 - headermod + (ic->large * 10)); 671 drawlegend(ic, 410 + (ic->large * 132), 155 - headermod + (ic->large * 40), 0); 672 673 drawsummary_digest(ic, 100, 30 - headermod, "day"); 674 drawsummary_digest(ic, 100, 29 + 7 * ic->lineheight - headermod, "month"); 675 676 switch (layout) { 677 // horizontal 678 case 1: 679 if (cfg.summarygraph == 1) { 680 drawfiveminutes(ic, 496 + (ic->large * 174), height - 30 - (ic->large * 8), rate, 422 + (ic->large * 154), height - 68 + headermod - (ic->large * 8)); 681 } else { 682 drawhours(ic, 500 + (ic->large * 160), 46 + (ic->large * 40) - headermod, rate); 683 } 684 break; 685 // vertical 686 case 2: 687 if (cfg.summarygraph == 1) { 688 drawfiveminutes(ic, 8 + (ic->large * 14), height - 31 - (ic->large * 6), rate, 422 + (ic->large * 154), 132 + (ic->large * 35)); 689 } else { 690 drawhours(ic, 12, 215 + (ic->large * 84) - headermod, rate); 691 } 692 break; 693 default: 694 break; 695 } 696 } 697 698 void drawsummary_alltime(IMAGECONTENT *ic, const int x, const int y) 699 { 700 struct tm *d; 701 char buffer[512], datebuff[16], daytemp[32]; 702 gdFontPtr titlefont; 703 704 if (ic->large) { 705 titlefont = gdFontGetGiant(); 706 } else { 707 titlefont = gdFontGetLarge(); 708 } 709 710 gdImageString(ic->im, titlefont, x + 12 + (ic->large * 10), y, (unsigned char *)"all time", ic->ctext); 711 snprintf(buffer, 4, "rx "); 712 strncat(buffer, getvalue(ic->interface.rxtotal, 12, RT_Normal), 32); 713 gdImageString(ic->im, ic->font, x, y + (2 * ic->lineheight), (unsigned char *)buffer, ic->ctext); 714 snprintf(buffer, 4, "tx "); 715 strncat(buffer, getvalue(ic->interface.txtotal, 12, RT_Normal), 32); 716 gdImageString(ic->im, ic->font, x, y + (3 * ic->lineheight), (unsigned char *)buffer, ic->ctext); 717 snprintf(buffer, 4, " = "); 718 strncat(buffer, getvalue(ic->interface.rxtotal + ic->interface.txtotal, 12, RT_Normal), 32); 719 gdImageString(ic->im, ic->font, x, y + (4 * ic->lineheight) + 2 + (ic->large * 4), (unsigned char *)buffer, ic->ctext); 720 d = localtime(&ic->interface.created); 721 strftime(datebuff, 16, cfg.tformat, d); 722 snprintf(daytemp, 24, "since %s", datebuff); 723 snprintf(buffer, 32, "%23s", daytemp); 724 gdImageString(ic->im, ic->font, x - 8 * ic->font->w, y + (5 * ic->lineheight) + 10 + (ic->large * 4), (unsigned char *)buffer, ic->ctext); 725 } 726 727 void drawsummary_digest(IMAGECONTENT *ic, const int x, const int y, const char *mode) 728 { 729 int textx, texty, offset = 0; 730 double rxp, txp, mod; 731 char buffer[512], datebuff[16], daytemp[32]; 732 time_t yesterday; 733 struct tm *d = NULL; 734 dbdatalist *datalist = NULL; 735 dbdatalist *data_current = NULL, *data_previous = NULL; 736 dbdatalistinfo datainfo; 737 gdFontPtr titlefont; 738 739 if (ic->large) { 740 titlefont = gdFontGetGiant(); 741 } else { 742 titlefont = gdFontGetLarge(); 743 } 744 745 yesterday = ic->current - 86400; 746 747 switch(mode[0]) { 748 case 'd': 749 break; 750 case 'm': 751 break; 752 default: 753 printf("Error: Unsupported mode %s for summary digest\n", mode); 754 return; 755 } 756 757 if (!db_getdata(&datalist, &datainfo, ic->interface.name, mode, 2) || datalist == NULL) { 758 gdImageString(ic->im, ic->font, 25 * ic->font->w, y + 30, (unsigned char *)"no data available", ic->ctext); 759 return; 760 } else if (datalist->next == NULL) { 761 data_current = datalist; 762 } else { 763 data_previous = datalist; 764 data_current = datalist->next; 765 } 766 767 /* latest entry */ 768 if (data_current->rx + data_current->tx == 0) { 769 rxp = txp = 0; 770 } else { 771 rxp = (double)data_current->rx / (double)(data_current->rx + data_current->tx) * 100; 772 txp = (double)100 - rxp; 773 } 774 775 /* do scaling if needed */ 776 if (data_previous != NULL && (data_current->rx + data_current->tx) < (data_previous->rx + data_previous->tx)) { 777 mod = (double)(data_current->rx + data_current->tx) / (double)(data_previous->rx + data_previous->tx); 778 rxp = rxp * mod; 779 txp = txp * mod; 780 } 781 782 /* move graph to center if there's only one to draw for this line */ 783 if (data_previous == NULL) { 784 offset = 85 + (ic->large * 25); 785 } 786 787 textx = x + offset; 788 texty = y; 789 790 drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3)); 791 792 if (mode[0] == 'd') { 793 /* get formatted date for today */ 794 d = localtime(&ic->current); 795 strftime(datebuff, 16, cfg.dformat, d); 796 797 /* get formatted date for current day in database */ 798 d = localtime(&data_current->timestamp); 799 strftime(daytemp, 16, cfg.dformat, d); 800 801 /* change daytemp to today if formatted days match */ 802 if (strcmp(datebuff, daytemp) == 0) { 803 strncpy_nt(daytemp, "today", 32); 804 } 805 } else if (mode[0] == 'm') { 806 d = localtime(&data_current->timestamp); 807 strftime(daytemp, 16, cfg.mformat, d); 808 } 809 810 snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); 811 gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext); 812 813 if (cfg.summaryrate) { 814 d = localtime(&ic->interface.updated); 815 if (mode[0] == 'd') { 816 snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, d->tm_sec + (d->tm_min * 60) + (d->tm_hour * 3600), 15)); 817 } else if (mode[0] == 'm') { 818 snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, mosecs(data_current->timestamp, ic->interface.updated), 15)); 819 } 820 gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext); 821 } else { 822 texty += 7; 823 } 824 825 snprintf(buffer, 4, "rx "); 826 strncat(buffer, getvalue(data_current->rx, 12, RT_Normal), 32); 827 gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext); 828 snprintf(buffer, 4, "tx "); 829 strncat(buffer, getvalue(data_current->tx, 12, RT_Normal), 32); 830 gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext); 831 snprintf(buffer, 4, " = "); 832 strncat(buffer, getvalue(data_current->rx + data_current->tx, 12, RT_Normal), 32); 833 gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext); 834 835 /* previous entry */ 836 if (data_previous != NULL) { 837 if (data_previous->rx + data_previous->tx == 0) { 838 rxp = txp = 0; 839 } else { 840 rxp = (double)data_previous->rx / (double)(data_previous->rx + data_previous->tx) * 100; 841 txp = (double)100 - rxp; 842 } 843 844 /* do scaling if needed */ 845 if ((data_previous->rx + data_previous->tx) < (data_current->rx + data_current->tx)) { 846 mod = (double)(data_previous->rx + data_previous->tx) / (double)(data_current->rx + data_current->tx); 847 rxp = rxp * mod; 848 txp = txp * mod; 849 } 850 851 textx += 180 + (ic->large * 60); 852 853 drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3)); 854 855 if (mode[0] == 'd') { 856 /* get formatted date for yesterday */ 857 d = localtime(&yesterday); 858 strftime(datebuff, 16, cfg.dformat, d); 859 860 /* get formatted date for previous day in database */ 861 d = localtime(&data_previous->timestamp); 862 strftime(daytemp, 16, cfg.dformat, d); 863 864 /* change daytemp to yesterday if formatted days match */ 865 if (strcmp(datebuff, daytemp) == 0) { 866 strncpy_nt(daytemp, "yesterday", 32); 867 } 868 } else if (mode[0] == 'm') { 869 d = localtime(&data_previous->timestamp); 870 strftime(daytemp, 16, cfg.mformat, d); 871 } 872 873 snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); 874 gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext); 875 876 if (cfg.summaryrate) { 877 if (mode[0] == 'd') { 878 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, 86400, 15)); 879 } else if (mode[0] == 'm') { 880 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, dmonth(d->tm_mon) * 86400, 15)); 881 } 882 gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext); 883 } else { 884 texty += 7; 885 } 886 887 snprintf(buffer, 4, "rx "); 888 strncat(buffer, getvalue(data_previous->rx, 12, RT_Normal), 32); 889 gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext); 890 snprintf(buffer, 4, "tx "); 891 strncat(buffer, getvalue(data_previous->tx, 12, RT_Normal), 32); 892 gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext); 893 snprintf(buffer, 4, " = "); 894 strncat(buffer, getvalue(data_previous->rx + data_previous->tx, 12, RT_Normal), 32); 895 gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext); 896 } 897 898 data_current = NULL; 899 data_previous = NULL; 900 dbdatalistfree(&datalist); 901 } 902 903 void drawfivegraph(IMAGECONTENT *ic, const int rate, const int resultcount, const int height) 904 { 905 int imagewidth, imageheight = height, headermod = 0; 906 907 imagewidth = resultcount + FIVEMINEXTRASPACE + (ic->large * 14); 908 909 if (!ic->showheader) { 910 headermod = 22; 911 } 912 913 imageinit(ic, imagewidth, imageheight); 914 layoutinit(ic, " / 5 minute", imagewidth, imageheight); 915 916 if (drawfiveminutes(ic, 8 + (ic->large * 14), imageheight - 30 - (ic->large * 8), rate, resultcount, imageheight - 68 + headermod - (ic->large * 8))) { 917 drawlegend(ic, imagewidth / 2 - (ic->large * 10), imageheight - 17 - (ic->large * 2), 0); 918 } 919 } 920 921 int drawfiveminutes(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate, const int resultcount, const int height) 922 { 923 int x = xpos, y = ypos, i = 0, t = 0, rxh = 0, txh = 0, step = 0, s = 0, prev = 0; 924 uint64_t scaleunit, max; 925 time_t timestamp; 926 double ratediv, e; 927 char buffer[32]; 928 struct tm *d; 929 dbdatalist *datalist = NULL, *datalist_i = NULL; 930 dbdatalistinfo datainfo; 931 gdFontPtr font; 932 933 if (ic->large) { 934 font = gdFontGetSmall(); 935 } else { 936 font = gdFontGetTiny(); 937 } 938 939 if (!db_getdata(&datalist, &datainfo, ic->interface.name, "fiveminute", (uint32_t)resultcount) || datainfo.count == 0) { 940 x = (resultcount + FIVEMINEXTRASPACE + (ic->large * 14)) / 2 - (8 * ic->font->w + ic->font->w / 2); 941 gdImageString(ic->im, ic->font, x, y - (height / 2) - ic->font->h, (unsigned char *)"no data available", ic->ctext); 942 return 0; 943 } 944 945 datalist_i = datalist; 946 947 if (cfg.rateunit) { 948 ratediv = 37.5; /* x * 8 / 300 */ 949 } else { 950 ratediv = 300; 951 } 952 953 /* axis */ 954 x += 36; 955 gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHFULLPADDING), y, ic->ctext); 956 gdImageLine(ic->im, x + 4, y + 4, x + 4, y - height, ic->ctext); 957 958 /* arrows */ 959 drawarrowup(ic, x + 4, y - 1 - height); 960 drawarrowright(ic, x + 1 + (resultcount + FIVEMINWIDTHFULLPADDING), y); 961 962 max = datainfo.maxrx + datainfo.maxtx; 963 964 if (datainfo.maxrx == datainfo.maxtx) { 965 txh = (int)((height - FIVEMINHEIGHTOFFSET * 2) / 2); 966 rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh; 967 max = (uint64_t)((double)datainfo.maxrx / ratediv); 968 t = rxh; 969 } else if (datainfo.maxrx > datainfo.maxtx) { 970 txh = (int)lrint(((double)datainfo.maxtx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2)); 971 rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh; 972 max = (uint64_t)((double)datainfo.maxrx / ratediv); 973 t = rxh; 974 } else { 975 rxh = (int)lrint(((double)datainfo.maxrx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2)); 976 txh = height - FIVEMINHEIGHTOFFSET * 2 - rxh; 977 max = (uint64_t)((double)datainfo.maxtx / ratediv); 978 t = txh; 979 } 980 981 /* center line */ 982 x += 5; 983 y -= txh + FIVEMINHEIGHTOFFSET; 984 gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHPADDING), y, ic->ctext); 985 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 4 - (ic->large * 3), (unsigned char *)" 0", ic->ctext); 986 987 /* scale values */ 988 scaleunit = getscale(max, rate); 989 990 s = (int)lrint(((double)scaleunit / (double)max) * t); 991 if (s == 0) { 992 s = 1; // force to show something when there's not much or any traffic, scale is likely to be wrong in this case 993 } 994 while (s * step < SCALEMINPIXELS) { 995 step++; 996 } 997 998 if (debug) { 999 printf("maxrx: %" PRIu64 "\n", datainfo.maxrx); 1000 printf("maxtx: %" PRIu64 "\n", datainfo.maxtx); 1001 printf("rxh: %d txh: %d\n", rxh, txh); 1002 printf("max divided: %" PRIu64 "\n", max); 1003 printf("scaleunit: %" PRIu64 "\nstep: %d\n", scaleunit, step); 1004 printf("pixels per step: %d\n", s); 1005 printf("mintime: %" PRIu64 "\nmaxtime: %" PRIu64 "\n", (uint64_t)datainfo.mintime, (uint64_t)datainfo.maxtime); 1006 printf("count: %u\n", datainfo.count); 1007 } 1008 1009 /* upper part scale values */ 1010 y--; // adjust to start above center line 1011 for (i = step; i * s <= rxh; i = i + step) { 1012 gdImageDashedLine(ic->im, x, y - (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y - (i * s), ic->cline); 1013 gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel); 1014 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext); 1015 prev = i * s; 1016 } 1017 if ((prev + (step * s) / 2) <= rxh) { 1018 gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel); 1019 } 1020 1021 y += 2; // adjust to start below center line 1022 prev = 0; 1023 1024 /* lower part scale values */ 1025 for (i = step; i * s <= txh; i = i + step) { 1026 gdImageDashedLine(ic->im, x, y + (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y + (i * s), ic->cline); 1027 gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel); 1028 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 + (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext); 1029 prev = i * s; 1030 } 1031 if ((prev + (step * s) / 2) <= txh) { 1032 gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel); 1033 } 1034 1035 y--; // y is now back on center line 1036 1037 /* scale text */ 1038 gdImageStringUp(ic->im, font, x - 39 - (ic->large * 14), ypos - height / 2 + (rate * 10), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext); 1039 1040 timestamp = datainfo.maxtime - (resultcount * 300); 1041 1042 while (datalist_i != NULL && datalist_i->timestamp < timestamp + 300) { 1043 if (debug) { 1044 printf("Skip data, %" PRIu64 " < %" PRIu64 "\n", (uint64_t)datalist_i->timestamp, (uint64_t)timestamp + 300); 1045 } 1046 datalist_i = datalist_i->next; 1047 } 1048 1049 for (i = 0; i < resultcount; i++) { 1050 1051 if (datalist_i == NULL) { 1052 break; 1053 } 1054 1055 timestamp += 300; 1056 d = localtime(×tamp); 1057 1058 if (d->tm_min == 0 && i > 2) { 1059 if (d->tm_hour % 2 == 0) { 1060 if (d->tm_hour == 0) { 1061 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cline); 1062 } else { 1063 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset); 1064 } 1065 1066 if (i > font->w) { 1067 snprintf(buffer, 32, "%02d", d->tm_hour); 1068 if (datalist_i->timestamp > timestamp) { 1069 gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->cline); 1070 } else { 1071 gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->ctext); 1072 } 1073 } 1074 } else { 1075 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset); 1076 } 1077 gdImageSetPixel(ic->im, x + i, y, ic->ctext); 1078 } 1079 1080 if (datalist_i->timestamp > timestamp) { 1081 gdImageSetPixel(ic->im, x + i, y, ic->cline); 1082 gdImageSetPixel(ic->im, x + i, y + txh + FIVEMINHEIGHTOFFSET, ic->cline); 1083 continue; 1084 } 1085 1086 /* only the last entry can be the currently ongoing period that may need scaling */ 1087 if (datalist_i->next == NULL && issametimeslot(LT_5min, datalist_i->timestamp, ic->interface.updated)) { 1088 e = (double)(ic->interface.updated - datalist_i->timestamp) / (double)300; 1089 if (e < 0.01) { 1090 e = 1; 1091 } 1092 } else { 1093 e = 1; 1094 } 1095 1096 t = (int)lrint(((double)datalist_i->rx / e / (double)datainfo.maxrx) * rxh); 1097 if (t > rxh) { 1098 t = rxh; 1099 } 1100 drawpole(ic, x + i, y - 1, t, 1, ic->crx); 1101 1102 t = (int)lrint(((double)datalist_i->tx / e / (double)datainfo.maxtx) * txh); 1103 if (t > txh) { 1104 t = txh; 1105 } 1106 drawpole(ic, x + i, y + 1, t, 2, ic->ctx); 1107 1108 datalist_i = datalist_i->next; 1109 } 1110 1111 dbdatalistfree(&datalist); 1112 1113 return 1; 1114 }