1 /*
2 * Copyright (C) 2021 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3 *
4 * This file is part of MooseFS.
5 *
6 * MooseFS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 (only).
9 *
10 * MooseFS is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MooseFS; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18 * or visit http://www.gnu.org/licenses/gpl-2.0.html
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #else
24 #define HAVE_ZLIB_H 1
25 #define HAVE_STRUCT_TM_TM_GMTOFF 1
26 #endif
27
28 #if defined(_THREAD_SAFE) || defined(_REENTRANT) || defined(_USE_PTHREADS)
29 # define USE_PTHREADS 1
30 #endif
31
32 #include <time.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <sys/time.h>
39 #include <errno.h>
40 #include <inttypes.h>
41 #ifdef HAVE_ZLIB_H
42 #include <zlib.h>
43 #endif
44 #ifdef USE_PTHREADS
45 #include <pthread.h>
46 #endif
47
48 #include "charts.h"
49 #include "crc.h"
50 #include "datapack.h"
51 #include "massert.h"
52 #include "slogger.h"
53
54 #define USE_NET_ORDER 1
55
56 #define MAXLENG 4096
57 #define MINLENG 100
58 #define MAXHEIGHT 1000
59 #define MINHEIGHT 100
60
61 #define XPOS 43
62 #define YPOS 6
63 #define XADD 50
64 #define YADD 20
65 #define MAXXSIZE (MAXLENG+XADD)
66 #define MAXYSIZE (MAXHEIGHT+YADD)
67
68 //#define LENG 950
69 //#define DATA 100
70 //#define XPOS 43
71 //#define YPOS 6
72 //#define XSIZE (LENG+50)
73 //#define YSIZE (DATA+20)
74 //#define LONGRATIO 6
75
76 #define SHORTRANGE 0
77 #define MEDIUMRANGE 1
78 #define LONGRANGE 2
79 #define VERYLONGRANGE 3
80
81 #define RANGES 4
82
83 #define CHARTS_DEF_IS_DIRECT(x) ((x)>=CHARTS_DIRECT_START && (x)<CHARTS_DIRECT_START+statdefscount)
84 #define CHARTS_DIRECT_POS(x) ((x)-CHARTS_DIRECT_START)
85 #define CHARTS_DEF_IS_CALC(x) ((x)>=CHARTS_CALC_START && (x)<CHARTS_CALC_START+calcdefscount)
86 #define CHARTS_CALC_POS(x) ((x)-CHARTS_CALC_START)
87
88 #define CHARTS_IS_DIRECT_STAT(x) ((x)<statdefscount)
89 #define CHARTS_EXTENDED_START 100
90 #define CHARTS_IS_EXTENDED_STAT(x) ((x)>=CHARTS_EXTENDED_START && (x)<CHARTS_EXTENDED_START+estatdefscount)
91 #define CHARTS_EXTENDED_POS(x) ((x)-CHARTS_EXTENDED_START)
92
93 static uint32_t *calcdefs;
94 static uint32_t **calcstartpos;
95 static uint32_t calcdefscount;
96 static statdef *statdefs;
97 static uint32_t statdefscount;
98 static estatdef *estatdefs;
99 static uint32_t estatdefscount;
100 static char* statsfilename;
101
102 #ifdef USE_PTHREADS
103 static pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
104 #endif
105
106 typedef uint64_t *stat_record[RANGES];
107
108 static stat_record *series;
109 static uint32_t pointers[RANGES];
110 static uint32_t timepoint[RANGES];
111
112 static uint64_t *monotonic;
113
114 //chart times (for subscripts)
115 static uint32_t shhour,shmin;
116 static uint32_t medhour,medmin;
117 static uint32_t lnghalfhour,lngmday,lngmonth,lngyear;
118 static uint32_t vlngmday,vlngmonth,vlngyear;
119
120 static uint8_t *chart;
121 static uint8_t *rawchart;
122 static uint8_t *compbuff;
123 static uint32_t rawchartsize = 0;
124 static uint32_t compbuffsize = 0;
125 static uint32_t compsize = 0;
126 #ifdef HAVE_ZLIB_H
127 static z_stream zstr;
128 #else
129 static uint8_t warning[50] = {
130 0x89,0xCF,0x83,0x8E,0x45,0xE7,0x9F,0x3C,0xF7,0xDE, /* 10001001 11001111 10000011 10001110 01000101 11100111 10011111 00111100 11110111 11011110 */
131 0xCA,0x22,0x04,0x51,0x6D,0x14,0x50,0x41,0x04,0x11, /* 11001010 00100010 00000100 01010001 01101101 00010100 01010000 01000001 00000100 00010001 */
132 0xAA,0x22,0x04,0x11,0x55,0xE7,0x9C,0x38,0xE7,0x11, /* 10101010 00100010 00000100 00010001 01010101 11100111 10011100 00111000 11100111 00010001 */
133 0x9A,0x22,0x04,0x51,0x45,0x04,0x50,0x04,0x14,0x11, /* 10011010 00100010 00000100 01010001 01000101 00000100 01010000 00000100 00010100 00010001 */
134 0x89,0xC2,0x03,0x8E,0x45,0x04,0x5F,0x79,0xE7,0xDE /* 10001001 11000010 00000011 10001110 01000101 00000100 01011111 01111001 11100111 11011110 */
135 };
136 #endif
137
138 #define COLOR_TRANSPARENT 0
139 #define COLOR_BKG 1
140 #define COLOR_AXIS 2
141 #define COLOR_AUX 3
142 #define COLOR_TEXT 4
143 #define COLOR_NODATA 5
144 #define COLOR_TEXTBKG 6
145 #if VERSMAJ==1
146 #define COLOR_DATA1 7
147 #define COLOR_DATA2 8
148 #define COLOR_DATA3 9
149 #endif
150
151 #define COLOR_DATA_BEGIN 50
152 #define COLOR_DATA_RANGE 50
153
154 static uint8_t data_colors[] = {
155 0x04,0xec,0xf1, // DATA1_MIN
156 0x23,0x86,0xb4, // DATA1_MAX
157 0x23,0x86,0xb4, // DATA2_MIN
158 0x15,0x2f,0x5f, // DATA2_MAX
159 0x15,0x2f,0x5f, // DATA3_MIN
160 0x01,0x04,0x2c // DATA3_MAX
161 };
162
163
164 static uint8_t png_header[] = {
165 137, 80, 78, 71, 13, 10, 26, 10, // signature
166
167 0, 0, 0, 13, 'I', 'H', 'D', 'R', // IHDR chunk
168 0, 0, 0, 0, 0, 0, 0, 0, // width and height (big endian)
169 8, 3, 0, 0, 0, // 8bits, indexed color mode, default compression, default filters, no interlace
170 'C', 'R', 'C', '#', // CRC32 placeholder
171
172 0, 0, 0x3, 0x0, 'P', 'L', 'T', 'E', // PLTE chunk
173 0xff,0xff,0xff, // color map 0 - background (transparent)
174 0xff,0xff,0xff, // color map 1 - chart background (white)
175 0x00,0x00,0x00, // color map 2 - axes (black)
176 0x00,0x00,0x00, // color map 3 - auxiliary lines (black)
177 0x5f,0x20,0x00, // color map 4 - texts (brown)
178 0xC0,0xC0,0xC0, // color map 5 - nodata (grey)
179 0xFF,0xFF,0xDE, // color map 6 - warning background (light yellow)
180 #if VERSMAJ==1
181 0x00,0xff,0x00, // color map 7 - data1 (light green)
182 0x00,0x96,0x00, // color map 8 - data2 (green)
183 0x00,0x60,0x00, // color map 9 - data3 (dark green)
184 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0A - c0F
185 #else
186 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c07 - c0F
187 #endif
188 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c10 - c1F
189 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c20 - c2F
190 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c30 - c3F
191 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c40 - c4F
192 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c50 - c5F
193 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c60 - c6F
194 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c70 - c7F
195 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c80 - c8F
196 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c90 - c9F
197 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cA0 - cAF
198 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cB0 - cBF
199 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cC0 - cCF
200 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cD0 - cDF
201 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cE0 - cEF
202 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // cF0 - cFF
203 'C', 'R', 'C', '#', // CRC32 placeholder
204
205 0, 0, 0, 1, 't', 'R', 'N', 'S', // tRNS chunk
206 0, // color 0 transparency - alpha = 0
207 'C', 'R', 'C', '#', // CRC32 placeholder
208
209 0, 0, 0, 1, 'b', 'K', 'G', 'D', // bKGD chunk
210 0, // color 0 = background
211 'C', 'R', 'C', '#', // CRC32 placeholder
212
213 0, 0, 0, 0, 'I', 'D', 'A', 'T' // IDAT chunk
214 };
215
216 static uint8_t png_tailer[] = {
217 'C', 'R', 'C', '#', // CRC32 placeholder
218 0, 0, 0, 0, 'I', 'E', 'N', 'D', // IEND chunk
219 'C', 'R', 'C', '#', // CRC32 placeholder
220 };
221
222 static uint8_t png_1x1[] = {
223 137, 80, 78, 71, 13, 10, 26, 10, // signature
224
225 0, 0, 0, 13, 'I', 'H', 'D', 'R', // IHDR chunk
226 0, 0, 0, 1, // width
227 0, 0, 0, 1, // height
228 8, 4, 0, 0, 0, // 8bits, grayscale with alpha color mode, default compression, default filters, no interlace
229 0xb5, 0x1c, 0x0c, 0x02, // CRC
230
231 0, 0, 0, 11, 'I', 'D', 'A', 'T', // IDAT chunk
232 0x08, 0xd7, 0x63, 0x60, 0x60, 0x00,
233 0x00, 0x00, 0x03, 0x00, 0x01,
234 0x20, 0xd5, 0x94, 0xc7, // CRC
235
236 0, 0, 0, 0, 'I', 'E', 'N', 'D', // IEND chunk
237 0xae, 0x42, 0x60, 0x82 // CRC
238 };
239
240 static uint8_t font[25][9]={
241 /* 01110 */
242 /* 10001 */
243 /* 10001 */
244 /* 10001 */
245 /* 10001 */
246 /* 10001 */
247 /* 01110 */
248 /* 00000 */
249 /* 00000 */
250 {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E,0x00,0x00},
251 /* 00100 */
252 /* 01100 */
253 /* 10100 */
254 /* 00100 */
255 /* 00100 */
256 /* 00100 */
257 /* 11111 */
258 /* 00000 */
259 /* 00000 */
260 {0x04,0x0C,0x14,0x04,0x04,0x04,0x1F,0x00,0x00},
261 /* 01110 */
262 /* 10001 */
263 /* 00001 */
264 /* 00010 */
265 /* 00100 */
266 /* 01000 */
267 /* 11111 */
268 /* 00000 */
269 /* 00000 */
270 {0x0E,0x11,0x01,0x02,0x04,0x08,0x1F,0x00,0x00},
271 /* 11111 */
272 /* 00010 */
273 /* 00100 */
274 /* 01110 */
275 /* 00001 */
276 /* 10001 */
277 /* 01110 */
278 /* 00000 */
279 /* 00000 */
280 {0x1F,0x02,0x04,0x0E,0x01,0x11,0x0E,0x00,0x00},
281 /* 00010 */
282 /* 00110 */
283 /* 01010 */
284 /* 10010 */
285 /* 11111 */
286 /* 00010 */
287 /* 00010 */
288 /* 00000 */
289 /* 00000 */
290 {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02,0x00,0x00},
291 /* 11111 */
292 /* 10000 */
293 /* 11110 */
294 /* 00001 */
295 /* 00001 */
296 /* 10001 */
297 /* 01110 */
298 /* 00000 */
299 /* 00000 */
300 {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E,0x00,0x00},
301 /* 00110 */
302 /* 01000 */
303 /* 10000 */
304 /* 11110 */
305 /* 10001 */
306 /* 10001 */
307 /* 01110 */
308 /* 00000 */
309 /* 00000 */
310 {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E,0x00,0x00},
311 /* 11111 */
312 /* 00001 */
313 /* 00010 */
314 /* 00010 */
315 /* 00100 */
316 /* 00100 */
317 /* 00100 */
318 /* 00000 */
319 /* 00000 */
320 {0x1F,0x01,0x02,0x02,0x04,0x04,0x04,0x00,0x00},
321 /* 01110 */
322 /* 10001 */
323 /* 10001 */
324 /* 01110 */
325 /* 10001 */
326 /* 10001 */
327 /* 01110 */
328 /* 00000 */
329 /* 00000 */
330 {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E,0x00,0x00},
331 /* 01110 */
332 /* 10001 */
333 /* 10001 */
334 /* 01111 */
335 /* 00001 */
336 /* 00010 */
337 /* 01100 */
338 /* 00000 */
339 /* 00000 */
340 {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C,0x00,0x00},
341 /* 00000 */
342 /* 00000 */
343 /* 00000 */
344 /* 00000 */
345 /* 00000 */
346 /* 00100 */
347 /* 00100 */
348 /* 00000 */
349 /* 00000 */
350 {0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x00,0x00},
351 /* 00000 */
352 /* 00000 */
353 /* 00100 */
354 /* 00000 */
355 /* 00100 */
356 /* 00000 */
357 /* 00000 */
358 /* 00000 */
359 /* 00000 */
360 {0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},
361 /* 01000 */
362 /* 01000 */
363 /* 01001 */
364 /* 01010 */
365 /* 01100 */
366 /* 01010 */
367 /* 01001 */
368 /* 00000 */
369 /* 00000 */
370 {0x08,0x08,0x09,0x0A,0x0C,0x0A,0x09,0x00,0x00},
371 /* 10001 */
372 /* 11011 */
373 /* 10101 */
374 /* 10001 */
375 /* 10001 */
376 /* 10001 */
377 /* 10001 */
378 /* 00000 */
379 /* 00000 */
380 {0x11,0x1B,0x15,0x11,0x11,0x11,0x11,0x00,0x00},
381 /* 01110 */
382 /* 10001 */
383 /* 10000 */
384 /* 10011 */
385 /* 10001 */
386 /* 10001 */
387 /* 01110 */
388 /* 00000 */
389 /* 00000 */
390 {0x0E,0x11,0x10,0x13,0x11,0x11,0x0E,0x00,0x00},
391 /* 11111 */
392 /* 00100 */
393 /* 00100 */
394 /* 00100 */
395 /* 00100 */
396 /* 00100 */
397 /* 00100 */
398 /* 00000 */
399 /* 00000 */
400 {0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x00},
401 /* 11110 */
402 /* 10001 */
403 /* 10001 */
404 /* 11110 */
405 /* 10000 */
406 /* 10000 */
407 /* 10000 */
408 /* 00000 */
409 /* 00000 */
410 {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10,0x00,0x00},
411 /* 11111 */
412 /* 10000 */
413 /* 10000 */
414 /* 11100 */
415 /* 10000 */
416 /* 10000 */
417 /* 11111 */
418 /* 00000 */
419 /* 00000 */
420 {0x1F,0x10,0x10,0x1C,0x10,0x10,0x1F,0x00,0x00},
421 /* 11111 */
422 /* 00001 */
423 /* 00010 */
424 /* 00100 */
425 /* 01000 */
426 /* 10000 */
427 /* 11111 */
428 /* 00000 */
429 /* 00000 */
430 {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F,0x00,0x00},
431 /* 10001 */
432 /* 10001 */
433 /* 01010 */
434 /* 00100 */
435 /* 00100 */
436 /* 00100 */
437 /* 00100 */
438 /* 00000 */
439 /* 00000 */
440 {0x11,0x11,0x0A,0x04,0x04,0x04,0x04,0x00,0x00},
441 /* 00000 */
442 /* 00000 */
443 /* 11110 */
444 /* 10101 */
445 /* 10101 */
446 /* 10101 */
447 /* 10101 */
448 /* 00000 */
449 /* 00000 */
450 {0x00,0x00,0x1E,0x15,0x15,0x15,0x15,0x00,0x00},
451 /* 00000 */
452 /* 00000 */
453 /* 10010 */
454 /* 10010 */
455 /* 10010 */
456 /* 10010 */
457 /* 11101 */
458 /* 10000 */
459 /* 10000 */
460 {0x00,0x00,0x12,0x12,0x12,0x12,0x1D,0x10,0x10},
461 /* 11001 */
462 /* 11010 */
463 /* 00010 */
464 /* 00100 */
465 /* 01000 */
466 /* 01011 */
467 /* 10011 */
468 /* 00000 */
469 /* 00000 */
470 {0x19,0x1A,0x02,0x04,0x08,0x0B,0x13,0x00,0x00},
471 /* 00000 */
472 /* 00000 */
473 /* 00000 */
474 /* 00000 */
475 /* 00000 */
476 /* 00000 */
477 /* 00000 */
478 /* 00000 */
479 /* 00000 */
480 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
481 /* 11111 */
482 /* 10001 */
483 /* 10001 */
484 /* 10001 */
485 /* 10001 */
486 /* 10001 */
487 /* 11111 */
488 /* 00000 */
489 /* 00000 */
490 {0x1F,0x11,0x11,0x11,0x11,0x11,0x1F,0x00,0x00}
491 };
492
493 #define FDOT 10
494 #define COLON 11
495 #define KILO 12
496 #define MEGA 13
497 #define GIGA 14
498 #define TERA 15
499 #define PETA 16
500 #define EXA 17
501 #define ZETTA 18
502 #define YOTTA 19
503 #define MILI 20
504 #define MICRO 21
505 #define PERCENT 22
506 #define SPACE 23
507 #define SQUARE 24
508
getmonleng(uint32_t year,uint32_t month)509 uint32_t getmonleng(uint32_t year,uint32_t month) {
510 switch (month) {
511 case 1:
512 case 3:
513 case 5:
514 case 7:
515 case 8:
516 case 10:
517 case 12:
518 return 31;
519 case 4:
520 case 6:
521 case 9:
522 case 11:
523 return 30;
524 case 2:
525 if (year%4) return 28;
526 if (year%100) return 29;
527 if (year%400) return 28;
528 return 29;
529 }
530 return 0;
531 }
532
533 #define CHARTS_FILE_VERSION 0x00010000
534
charts_store(void)535 void charts_store (void) {
536 int fd;
537 uint32_t s,i,j,p;
538 uint64_t *tab;
539 #ifdef USE_NET_ORDER
540 uint8_t *ptr;
541 uint8_t hdr[16];
542 uint8_t data[8*MAXLENG];
543 #else
544 uint32_t hdr[4];
545 #endif
546 char namehdr[100];
547
548 #ifdef USE_PTHREADS
549 zassert(pthread_mutex_lock(&glock));
550 #endif
551
552 fd = open(statsfilename,O_WRONLY | O_TRUNC | O_CREAT,0666);
553 if (fd<0) {
554 mfs_errlog(LOG_WARNING,"error creating charts data file");
555 #ifdef USE_PTHREADS
556 zassert(pthread_mutex_unlock(&glock));
557 #endif
558 return;
559 }
560 #ifdef USE_NET_ORDER
561 ptr = hdr;
562 put32bit(&ptr,CHARTS_FILE_VERSION);
563 put32bit(&ptr,MAXLENG);
564 put32bit(&ptr,statdefscount);
565 put32bit(&ptr,timepoint[SHORTRANGE]);
566 if (write(fd,(void*)hdr,16)!=16) {
567 mfs_errlog(LOG_WARNING,"error writing charts data file");
568 close(fd);
569 #ifdef USE_PTHREADS
570 zassert(pthread_mutex_unlock(&glock));
571 #endif
572 return;
573 }
574 #else
575 hdr[0]=CHARTS_FILE_VERSION;
576 hdr[1]=MAXLENG;
577 hdr[2]=statdefscount;
578 hdr[3]=timepoint[SHORTRANGE];
579 if (write(fd,(void*)hdr,sizeof(uint32_t)*4)!=sizeof(uint32_t)*4) {
580 mfs_errlog(LOG_WARNING,"error writing charts data file");
581 close(fd);
582 #ifdef USE_PTHREADS
583 zassert(pthread_mutex_unlock(&glock));
584 #endif
585 return;
586 }
587 #endif
588 for (i=0 ; i<statdefscount ; i++) {
589 s = strlen(statdefs[i].name);
590 memset(namehdr,0,100);
591 memcpy(namehdr,statdefs[i].name,(s>100)?100:s);
592 if (write(fd,(void*)namehdr,100)!=100) {
593 mfs_errlog(LOG_WARNING,"error writing charts data file");
594 close(fd);
595 #ifdef USE_PTHREADS
596 zassert(pthread_mutex_unlock(&glock));
597 #endif
598 return;
599 }
600 for (j=0 ; j<RANGES ; j++) {
601 tab = series[i][j];
602 p = pointers[j]+1;
603 #ifdef USE_NET_ORDER
604 ptr = data;
605 for (s=0 ; s<MAXLENG ; s++) {
606 put64bit(&ptr,tab[(p+s)%MAXLENG]);
607 }
608 if (write(fd,(void*)data,8*MAXLENG)!=(ssize_t)(8*MAXLENG)) {
609 mfs_errlog(LOG_WARNING,"error writing charts data file");
610 close(fd);
611 #ifdef USE_PTHREADS
612 zassert(pthread_mutex_unlock(&glock));
613 #endif
614 return;
615 }
616 #else
617 if (p<MAXLENG) {
618 if (write(fd,(void*)(tab+p),sizeof(uint64_t)*(MAXLENG-p))!=(ssize_t)(sizeof(uint64_t)*(MAXLENG-p))) {
619 mfs_errlog(LOG_WARNING,"error writing charts data file");
620 close(fd);
621 #ifdef USE_PTHREADS
622 zassert(pthread_mutex_unlock(&glock));
623 #endif
624 return;
625 }
626 }
627 if (write(fd,(void*)tab,sizeof(uint64_t)*p)!=(ssize_t)(sizeof(uint64_t)*p)) {
628 mfs_errlog(LOG_WARNING,"error writing charts data file");
629 close(fd);
630 #ifdef USE_PTHREADS
631 zassert(pthread_mutex_unlock(&glock));
632 #endif
633 return;
634 }
635 #endif
636 }
637 }
638 close(fd);
639 #ifdef USE_PTHREADS
640 zassert(pthread_mutex_unlock(&glock));
641 #endif
642 }
643
644
charts_load(uint8_t mode)645 int charts_load(uint8_t mode) {
646 int fd;
647 uint32_t i,j,k,fleng,fcharts;
648 uint64_t *tab;
649 #ifdef USE_NET_ORDER
650 uint32_t l;
651 const uint8_t *ptr;
652 uint8_t hdr[16];
653 uint8_t data[8*MAXLENG];
654 #else
655 uint32_t hdr[3];
656 #endif
657 char namehdr[101];
658
659 fd = open(statsfilename,O_RDONLY);
660 if (fd<0) {
661 if (mode==1) {
662 fprintf(stderr,"file loading error: %s\n",strerr(errno));
663 return -1;
664 }
665 if (errno!=ENOENT) {
666 mfs_errlog(LOG_WARNING,"error reading charts data file");
667 } else {
668 mfs_syslog(LOG_NOTICE,"no charts data file - initializing empty charts");
669 }
670 return 0;
671 }
672 #ifdef USE_NET_ORDER
673 if (read(fd,(void*)hdr,16)!=16) {
674 if (mode==1) {
675 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
676 close(fd);
677 return -1;
678 }
679 mfs_errlog(LOG_WARNING,"error reading charts data file");
680 close(fd);
681 return 0;
682 }
683 ptr = hdr;
684 i = get32bit(&ptr);
685 if (i!=CHARTS_FILE_VERSION) {
686 if (mode==1) {
687 fprintf(stderr,"unrecognized charts data file format\n");
688 close(fd);
689 return -1;
690 }
691 mfs_syslog(LOG_WARNING,"unrecognized charts data file format - initializing empty charts");
692 close(fd);
693 return 0;
694 }
695 fleng = get32bit(&ptr);
696 fcharts = get32bit(&ptr);
697 i = get32bit(&ptr);
698 timepoint[SHORTRANGE]=i;
699 // timepoint[MEDIUMRANGE]=i/6;
700 // timepoint[LONGRANGE]=i/30;
701 // timepoint[VERYLONGRANGE]=i/(24*60);
702 #else
703 if (read(fd,(void*)hdr,sizeof(uint32_t))!=sizeof(uint32_t)) {
704 if (mode==1) {
705 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
706 close(fd);
707 return -1;
708 }
709 mfs_errlog(LOG_WARNING,"error reading charts data file");
710 close(fd);
711 return 0;
712 }
713 if (hdr[0]!=CHARTS_FILE_VERSION) {
714 if (mode==1) {
715 fprintf(stderr,"unrecognized charts data file format\n");
716 close(fd);
717 return -1;
718 }
719 mfs_syslog(LOG_WARNING,"unrecognized charts data file format - initializing empty charts");
720 close(fd);
721 return 0;
722 }
723 if (read(fd,(void*)hdr,sizeof(uint32_t)*3)!=sizeof(uint32_t)*3) {
724 if (mode==1) {
725 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
726 close(fd);
727 return -1;
728 }
729 mfs_errlog(LOG_WARNING,"error reading charts data file");
730 close(fd);
731 return 0;
732 }
733 fleng = hdr[0];
734 fcharts = hdr[1];
735 timepoint[SHORTRANGE]=hdr[2];
736 // timepoint[MEDIUMRANGE]=hdr[2]/6;
737 // timepoint[LONGRANGE]=hdr[2]/30;
738 // timepoint[VERYLONGRANGE]=hdr[2]/(24*60);
739 #endif
740 pointers[SHORTRANGE]=MAXLENG-1;
741 pointers[MEDIUMRANGE]=MAXLENG-1;
742 pointers[LONGRANGE]=MAXLENG-1;
743 pointers[VERYLONGRANGE]=MAXLENG-1;
744 for (i=0 ; i<fcharts ; i++) {
745 if (read(fd,namehdr,100)!=100) {
746 if (mode==1) {
747 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
748 close(fd);
749 return -1;
750 }
751 mfs_errlog(LOG_WARNING,"error reading charts data file");
752 close(fd);
753 return 0;
754 }
755 namehdr[100]=0;
756 for (j=0 ; j<statdefscount && strcmp(statdefs[j].name,namehdr)!=0 ; j++) {}
757 if (j>=statdefscount) {
758 lseek(fd,RANGES*fleng*8,SEEK_CUR);
759 // ignore data
760 } else {
761 for (k=0 ; k<RANGES ; k++) {
762 tab = series[j][k];
763 if (fleng>MAXLENG) {
764 lseek(fd,(fleng-MAXLENG)*sizeof(uint64_t),SEEK_CUR);
765 }
766 #ifdef USE_NET_ORDER
767 if (fleng<MAXLENG) {
768 if (read(fd,(void*)data,8*fleng)!=(ssize_t)(8*fleng)) {
769 if (mode==1) {
770 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
771 close(fd);
772 return -1;
773 }
774 mfs_errlog(LOG_WARNING,"error reading charts data file");
775 close(fd);
776 return 0;
777 }
778 ptr = data;
779 for (l=MAXLENG-fleng ; l<MAXLENG ; l++) {
780 tab[l] = get64bit(&ptr);
781 }
782 } else {
783 if (read(fd,(void*)data,8*MAXLENG)!=(ssize_t)(8*MAXLENG)) {
784 if (mode==1) {
785 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
786 close(fd);
787 return -1;
788 }
789 mfs_errlog(LOG_WARNING,"error reading charts data file");
790 close(fd);
791 return 0;
792 }
793 ptr = data;
794 for (l=0 ; l<MAXLENG ; l++) {
795 tab[l] = get64bit(&ptr);
796 }
797 }
798 #else
799 if (fleng<MAXLENG) {
800 if (read(fd,(void*)(tab+(MAXLENG-fleng)),sizeof(uint64_t)*fleng)!=(ssize_t)(sizeof(uint64_t)*fleng)) {
801 if (mode==1) {
802 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
803 close(fd);
804 return -1;
805 }
806 mfs_errlog(LOG_WARNING,"error reading charts data file");
807 close(fd);
808 return 0;
809 }
810 } else {
811 if (read(fd,(void*)tab,sizeof(uint64_t)*MAXLENG)!=(ssize_t)(sizeof(uint64_t)*MAXLENG)) {
812 if (mode==1) {
813 fprintf(stderr,"error reading charts data file: %s\n",strerr(errno));
814 close(fd);
815 return -1;
816 }
817 mfs_errlog(LOG_WARNING,"error reading charts data file");
818 close(fd);
819 return 0;
820 }
821 }
822 #endif
823 }
824 }
825 }
826 close(fd);
827 if (mode==1) {
828 return 0;
829 }
830 mfs_syslog(LOG_NOTICE,"stats file has been loaded");
831 return 0;
832 }
833
charts_filltab(uint64_t * datatab,uint32_t range,uint32_t type,uint32_t cno,uint32_t width)834 uint8_t charts_filltab(uint64_t *datatab,uint32_t range,uint32_t type,uint32_t cno,uint32_t width) {
835 #if defined(INT64_MIN)
836 # define STACK_NODATA INT64_MIN
837 #elif defined(INT64_C)
838 # define STACK_NODATA (-INT64_C(9223372036854775807)-1)
839 #else
840 # define STACK_NODATA (-9223372036854775807LL-1)
841 #endif
842
843 uint32_t i,j;
844 uint32_t pointer;
845 uint32_t src,*ops;
846 int64_t stack[50];
847 uint32_t sp;
848
849 if (range>=RANGES || cno==0 || cno>3) {
850 return 0;
851 }
852 pointer = pointers[range];
853 if (CHARTS_IS_DIRECT_STAT(type)) {
854 if (cno==1) {
855 for (i=0 ; i<width ; i++) {
856 j = (MAXLENG-width+1+pointer+i)%MAXLENG;
857 datatab[i] = series[type][range][j];
858 }
859 return 1;
860 }
861 } else if (CHARTS_IS_EXTENDED_STAT(type)) {
862 if (cno==1) {
863 src = estatdefs[CHARTS_EXTENDED_POS(type)].c1src;
864 } else if (cno==2) {
865 src = estatdefs[CHARTS_EXTENDED_POS(type)].c2src;
866 } else {
867 src = estatdefs[CHARTS_EXTENDED_POS(type)].c3src;
868 }
869 if (CHARTS_DEF_IS_DIRECT(src)) {
870 for (i=0 ; i<width ; i++) {
871 j = (MAXLENG-width+1+pointer+i)%MAXLENG;
872 datatab[i] = series[CHARTS_DIRECT_POS(src)][range][j];
873 }
874 return 1;
875 } else if (CHARTS_DEF_IS_CALC(src)) {
876 for (i=0 ; i<width ; i++) {
877 j = (MAXLENG-width+1+pointer+i)%MAXLENG;
878 sp=0;
879 ops = calcstartpos[CHARTS_CALC_POS(src)];
880 while (*ops!=CHARTS_OP_END) {
881 if (CHARTS_IS_DIRECT_STAT(*ops)) {
882 if (sp<50) {
883 if (series[*ops][range][j]==CHARTS_NODATA) {
884 stack[sp]=STACK_NODATA;
885 } else {
886 stack[sp]=series[*ops][range][j];
887 }
888 sp++;
889 }
890 } else if (*ops==CHARTS_OP_ADD) {
891 if (sp>=2) {
892 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA) {
893 stack[sp-2]=STACK_NODATA;
894 } else {
895 stack[sp-2]+=stack[sp-1];
896 }
897 sp--;
898 }
899 } else if (*ops==CHARTS_OP_SUB) {
900 if (sp>=2) {
901 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA) {
902 stack[sp-2]=STACK_NODATA;
903 } else {
904 stack[sp-2]-=stack[sp-1];
905 }
906 sp--;
907 }
908 } else if (*ops==CHARTS_OP_MIN) {
909 if (sp>=2) {
910 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA) {
911 stack[sp-2]=STACK_NODATA;
912 } else if (stack[sp-1]<stack[sp-2]) {
913 stack[sp-2]=stack[sp-1];
914 }
915 sp--;
916 }
917 } else if (*ops==CHARTS_OP_MAX) {
918 if (sp>=2) {
919 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA) {
920 stack[sp-2]=STACK_NODATA;
921 } else if (stack[sp-1]>stack[sp-2]) {
922 stack[sp-2]=stack[sp-1];
923 }
924 sp--;
925 }
926 } else if (*ops==CHARTS_OP_MUL) {
927 if (sp>=2) {
928 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA) {
929 stack[sp-2]=STACK_NODATA;
930 } else {
931 stack[sp-2]*=stack[sp-1];
932 }
933 sp--;
934 }
935 } else if (*ops==CHARTS_OP_DIV) {
936 if (sp>=2) {
937 if (stack[sp-1]==STACK_NODATA || stack[sp-2]==STACK_NODATA || stack[sp-1]==0) {
938 stack[sp-2]=STACK_NODATA;
939 } else {
940 stack[sp-2]/=stack[sp-1];
941 }
942 sp--;
943 }
944 } else if (*ops==CHARTS_OP_NEG) {
945 if (sp>=1) {
946 if (stack[sp-1]!=STACK_NODATA) {
947 stack[sp-1]=-stack[sp-1];
948 }
949 }
950 } else if (*ops==CHARTS_OP_CONST) {
951 ops++;
952 if (sp<50) {
953 stack[sp]=*ops;
954 sp++;
955 }
956 }
957 ops++;
958 }
959 if (sp>=1 && stack[sp-1]>=0) { // STACK_NODATA < 0, so this condition is enough for STACK_NODATA
960 datatab[i]=stack[sp-1];
961 } else {
962 datatab[i]=CHARTS_NODATA;
963 }
964 }
965 return 1;
966 }
967 }
968 return 0;
969 }
970
charts_get(uint32_t type,uint32_t numb)971 uint64_t charts_get (uint32_t type,uint32_t numb) {
972 uint64_t result=0,cnt;
973 uint64_t *tab;
974 uint32_t i,j;
975
976 if (numb==0 || numb>MAXLENG) {
977 return result;
978 }
979 if (CHARTS_IS_DIRECT_STAT(type)) {
980 #ifdef USE_PTHREADS
981 zassert(pthread_mutex_lock(&glock));
982 #endif
983 tab = series[type][SHORTRANGE];
984 j = pointers[SHORTRANGE] % MAXLENG;
985 if (statdefs[type].mode == CHARTS_MODE_ADD) {
986 cnt=0;
987 for (i=0 ; i<numb ; i++) {
988 if (tab[j]!=CHARTS_NODATA) {
989 result += tab[j];
990 cnt++;
991 }
992 if (j>0) {
993 j--;
994 } else {
995 j = MAXLENG-1;
996 }
997 }
998 if (cnt>0) {
999 result /= cnt;
1000 }
1001 } else {
1002 for (i=0 ; i<numb ; i++) {
1003 if (tab[j]!=CHARTS_NODATA && tab[j]>result) {
1004 result = tab[j];
1005 }
1006 if (j>0) {
1007 j--;
1008 } else {
1009 j = MAXLENG-1;
1010 }
1011 }
1012 }
1013 #ifdef USE_PTHREADS
1014 zassert(pthread_mutex_unlock(&glock));
1015 #endif
1016 }
1017 return result;
1018 }
1019
charts_inittimepointers(void)1020 void charts_inittimepointers (void) {
1021 time_t now;
1022 int32_t local;
1023 struct tm *ts;
1024
1025 if (timepoint[SHORTRANGE]==0) {
1026 now = time(NULL);
1027 ts = localtime(&now);
1028 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
1029 local = now+ts->tm_gmtoff;
1030 #else
1031 local = now;
1032 #endif
1033 } else {
1034 now = timepoint[SHORTRANGE]*60;
1035 ts = gmtime(&now);
1036 local = now;
1037 }
1038
1039 timepoint[SHORTRANGE] = local / 60;
1040 shmin = ts->tm_min;
1041 shhour = ts->tm_hour;
1042 timepoint[MEDIUMRANGE] = local / (60 * 6);
1043 medmin = ts->tm_min;
1044 medhour = ts->tm_hour;
1045 timepoint[LONGRANGE] = local / (60 * 30);
1046 lnghalfhour = ts->tm_hour*2;
1047 if (ts->tm_min>=30) {
1048 lnghalfhour++;
1049 }
1050 lngmday = ts->tm_mday;
1051 lngmonth = ts->tm_mon + 1;
1052 lngyear = ts->tm_year + 1900;
1053 timepoint[VERYLONGRANGE] = local / (60 * 60 * 24);
1054 vlngmday = ts->tm_mday;
1055 vlngmonth = ts->tm_mon + 1;
1056 vlngyear = ts->tm_year + 1900;
1057 }
1058
charts_add(uint64_t * data,uint32_t datats)1059 void charts_add (uint64_t *data,uint32_t datats) {
1060 uint32_t i,j;
1061 struct tm *ts;
1062 time_t now = datats;
1063 int32_t local;
1064
1065 int32_t nowtime,delta;
1066
1067 if (data) {
1068 for (j=0 ; j<statdefscount ; j++) {
1069 monotonic[j] += data[j];
1070 }
1071 }
1072
1073 ts = localtime(&now);
1074 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
1075 local = now+ts->tm_gmtoff;
1076 #else
1077 local = now;
1078 #endif
1079
1080 #ifdef USE_PTHREADS
1081 zassert(pthread_mutex_lock(&glock));
1082 #endif
1083 // short range chart - every 1 min
1084
1085 nowtime = local / 60;
1086
1087 delta = nowtime - timepoint[SHORTRANGE];
1088
1089 if (delta>0) {
1090 if (delta>MAXLENG) {
1091 delta=MAXLENG;
1092 }
1093 while (delta>0) {
1094 pointers[SHORTRANGE]++;
1095 pointers[SHORTRANGE]%=MAXLENG;
1096 for (i=0 ; i<statdefscount ; i++) {
1097 series[i][SHORTRANGE][pointers[SHORTRANGE]] = CHARTS_NODATA;
1098 }
1099 delta--;
1100 }
1101 timepoint[SHORTRANGE] = nowtime;
1102 shmin = ts->tm_min;
1103 shhour = ts->tm_hour;
1104 }
1105 if (delta<=0 && delta>-MAXLENG && data) {
1106 i = (pointers[SHORTRANGE] + MAXLENG + delta) % MAXLENG;
1107 for (j=0 ; j<statdefscount ; j++) {
1108 if (series[j][SHORTRANGE][i]==CHARTS_NODATA) { // no data
1109 series[j][SHORTRANGE][i] = data[j];
1110 } else if (statdefs[j].mode==CHARTS_MODE_ADD) { // add mode
1111 series[j][SHORTRANGE][i] += data[j];
1112 } else if (data[j]>series[j][SHORTRANGE][i]) { // max mode
1113 series[j][SHORTRANGE][i] = data[j];
1114 }
1115 }
1116 }
1117
1118 // medium range chart - every 6 min
1119
1120 nowtime = local / (60 * 6);
1121
1122 delta = nowtime - timepoint[MEDIUMRANGE];
1123
1124 if (delta>0) {
1125 if (delta>MAXLENG) {
1126 delta=MAXLENG;
1127 }
1128 while (delta>0) {
1129 pointers[MEDIUMRANGE]++;
1130 pointers[MEDIUMRANGE]%=MAXLENG;
1131 for (i=0 ; i<statdefscount ; i++) {
1132 series[i][MEDIUMRANGE][pointers[MEDIUMRANGE]] = CHARTS_NODATA;
1133 }
1134 delta--;
1135 }
1136 timepoint[MEDIUMRANGE] = nowtime;
1137 medmin = ts->tm_min;
1138 medhour = ts->tm_hour;
1139 }
1140 if (delta<=0 && delta>-MAXLENG && data) {
1141 i = (pointers[MEDIUMRANGE] + MAXLENG + delta) % MAXLENG;
1142 for (j=0 ; j<statdefscount ; j++) {
1143 if (series[j][MEDIUMRANGE][i]==CHARTS_NODATA) { // no data
1144 series[j][MEDIUMRANGE][i] = data[j];
1145 } else if (statdefs[j].mode==CHARTS_MODE_ADD) { // add mode
1146 series[j][MEDIUMRANGE][i] += data[j];
1147 } else if (data[j]>series[j][MEDIUMRANGE][i]) { // max mode
1148 series[j][MEDIUMRANGE][i] = data[j];
1149 }
1150 }
1151 }
1152
1153
1154 // long range chart - every 30 min
1155
1156 nowtime = local / (60 * 30);
1157
1158 delta = nowtime - timepoint[LONGRANGE];
1159
1160 if (delta>0) {
1161 if (delta>MAXLENG) {
1162 delta=MAXLENG;
1163 }
1164 while (delta>0) {
1165 pointers[LONGRANGE]++;
1166 pointers[LONGRANGE]%=MAXLENG;
1167 for (i=0 ; i<statdefscount ; i++) {
1168 series[i][LONGRANGE][pointers[LONGRANGE]] = CHARTS_NODATA;
1169 }
1170 delta--;
1171 }
1172 timepoint[LONGRANGE] = nowtime;
1173 lnghalfhour = ts->tm_hour*2;
1174 if (ts->tm_min>=30) {
1175 lnghalfhour++;
1176 }
1177 lngmday = ts->tm_mday;
1178 lngmonth = ts->tm_mon + 1;
1179 lngyear = ts->tm_year + 1900;
1180 }
1181 if (delta<=0 && delta>-MAXLENG && data) {
1182 i = (pointers[LONGRANGE] + MAXLENG + delta) % MAXLENG;
1183 for (j=0 ; j<statdefscount ; j++) {
1184 if (series[j][LONGRANGE][i]==CHARTS_NODATA) { // no data
1185 series[j][LONGRANGE][i] = data[j];
1186 } else if (statdefs[j].mode==CHARTS_MODE_ADD) { // add mode
1187 series[j][LONGRANGE][i] += data[j];
1188 } else if (data[j]>series[j][LONGRANGE][i]) { // max mode
1189 series[j][LONGRANGE][i] = data[j];
1190 }
1191 }
1192 }
1193 // long range chart - every 1 day
1194
1195 nowtime = local / (60 * 60 * 24);
1196
1197 delta = nowtime - timepoint[VERYLONGRANGE];
1198
1199 if (delta>0) {
1200 if (delta>MAXLENG) {
1201 delta=MAXLENG;
1202 }
1203 while (delta>0) {
1204 pointers[VERYLONGRANGE]++;
1205 pointers[VERYLONGRANGE]%=MAXLENG;
1206 for (i=0 ; i<statdefscount ; i++) {
1207 series[i][VERYLONGRANGE][pointers[VERYLONGRANGE]] = CHARTS_NODATA;
1208 }
1209 delta--;
1210 }
1211 timepoint[VERYLONGRANGE] = nowtime;
1212 vlngmday = ts->tm_mday;
1213 vlngmonth = ts->tm_mon + 1;
1214 vlngyear = ts->tm_year + 1900;
1215 }
1216 if (delta<=0 && delta>-MAXLENG && data) {
1217 i = (pointers[VERYLONGRANGE] + MAXLENG + delta) % MAXLENG;
1218 for (j=0 ; j<statdefscount ; j++) {
1219 if (series[j][VERYLONGRANGE][i]==CHARTS_NODATA) { // no data
1220 series[j][VERYLONGRANGE][i] = data[j];
1221 } else if (statdefs[j].mode==CHARTS_MODE_ADD) { // add mode
1222 series[j][VERYLONGRANGE][i] += data[j];
1223 } else if (data[j]>series[j][VERYLONGRANGE][i]) { // max mode
1224 series[j][VERYLONGRANGE][i] = data[j];
1225 }
1226 }
1227 }
1228 #ifdef USE_PTHREADS
1229 zassert(pthread_mutex_unlock(&glock));
1230 #endif
1231 }
1232
charts_term(void)1233 void charts_term (void) {
1234 uint32_t i,j;
1235 #ifdef USE_PTHREADS
1236 zassert(pthread_mutex_lock(&glock));
1237 zassert(pthread_mutex_unlock(&glock));
1238 #endif
1239 free(statsfilename);
1240 if (calcdefs) {
1241 free(calcdefs);
1242 }
1243 if (calcstartpos) {
1244 free(calcstartpos);
1245 }
1246 if (estatdefs) {
1247 free(estatdefs);
1248 }
1249 for (i=0 ; i<statdefscount ; i++) {
1250 free(statdefs[i].name);
1251 }
1252 if (statdefs) {
1253 free(statdefs);
1254 }
1255 for (i=0 ; i<statdefscount ; i++) {
1256 for (j=0 ; j<RANGES ; j++) {
1257 if (series[i][j]) {
1258 free(series[i][j]);
1259 }
1260 }
1261 }
1262 if (series) {
1263 free(series);
1264 }
1265 if (monotonic) {
1266 free(monotonic);
1267 }
1268 if (compbuff) {
1269 free(compbuff);
1270 }
1271 if (rawchart) {
1272 free(rawchart);
1273 }
1274 if (chart) {
1275 free(chart);
1276 }
1277 #ifdef HAVE_ZLIB_H
1278 deflateEnd(&zstr);
1279 #endif
1280 }
1281
png_make_palette(void)1282 static inline void png_make_palette(void) {
1283 uint32_t rng,indx;
1284 double r,g,b,dr,db,dg;
1285 for (rng=0 ; rng<3 ; rng++) {
1286 r = data_colors[rng*6+0];
1287 g = data_colors[rng*6+1];
1288 b = data_colors[rng*6+2];
1289 dr = data_colors[rng*6+3];
1290 dg = data_colors[rng*6+4];
1291 db = data_colors[rng*6+5];
1292 dr -= r;
1293 dg -= g;
1294 db -= b;
1295 dr /= COLOR_DATA_RANGE;
1296 dg /= COLOR_DATA_RANGE;
1297 db /= COLOR_DATA_RANGE;
1298 r += 0.5;
1299 g += 0.5;
1300 b += 0.5;
1301 for (indx = 0 ; indx < COLOR_DATA_RANGE ; indx++) {
1302 png_header[41+(COLOR_DATA_BEGIN+rng*COLOR_DATA_RANGE+indx)*3+0] = r;
1303 png_header[41+(COLOR_DATA_BEGIN+rng*COLOR_DATA_RANGE+indx)*3+1] = g;
1304 png_header[41+(COLOR_DATA_BEGIN+rng*COLOR_DATA_RANGE+indx)*3+2] = b;
1305 r+=dr;
1306 g+=dg;
1307 b+=db;
1308 }
1309 }
1310 }
1311
charts_init(const uint32_t * calcs,const statdef * stats,const estatdef * estats,const char * filename,uint8_t mode)1312 int charts_init (const uint32_t *calcs,const statdef *stats,const estatdef *estats,const char *filename,uint8_t mode) {
1313 uint32_t i,j;
1314
1315 chart = malloc(MAXXSIZE*MAXYSIZE);
1316 passert(chart);
1317 rawchartsize = (1+MAXXSIZE)*(MAXYSIZE);
1318 rawchart = malloc(rawchartsize);
1319 passert(rawchart);
1320 compbuffsize = ((uint64_t)rawchartsize*UINT64_C(1001))/UINT64_C(1000)+16;
1321 compbuff = malloc(compbuffsize);
1322 passert(compbuff);
1323
1324 statsfilename = strdup(filename);
1325 passert(statsfilename);
1326
1327 for (i=0,calcdefscount=0 ; calcs[i]!=CHARTS_DEFS_END ; i++) {
1328 if (calcs[i]==CHARTS_OP_END) {
1329 calcdefscount++;
1330 }
1331 }
1332 if (i>0 && calcdefscount>0) {
1333 calcdefs = (uint32_t*)malloc(sizeof(uint32_t)*i);
1334 passert(calcdefs);
1335 calcstartpos = (uint32_t**)malloc(sizeof(uint32_t*)*calcdefscount);
1336 passert(calcstartpos);
1337 j=0;
1338 calcstartpos[j]=calcdefs;
1339 j++;
1340 for (i=0 ; calcs[i]!=CHARTS_DEFS_END ; i++) {
1341 calcdefs[i] = calcs[i];
1342 if (calcs[i]==CHARTS_OP_END) {
1343 if (j<calcdefscount) {
1344 calcstartpos[j]=calcdefs+i+1;
1345 j++;
1346 }
1347 }
1348 }
1349 } else {
1350 calcdefs = NULL;
1351 calcstartpos = NULL;
1352 }
1353 for (statdefscount=0 ; stats[statdefscount].divisor ; statdefscount++) {}
1354 if (statdefscount>0) {
1355 statdefs = (statdef*)malloc(sizeof(statdef)*statdefscount);
1356 passert(statdefs);
1357 } else {
1358 statdefs = NULL;
1359 }
1360 for (i=0 ; i<statdefscount ; i++) {
1361 statdefs[i].name = strdup(stats[i].name);
1362 passert(statdefs[i].name);
1363 statdefs[i].statid = stats[i].statid;
1364 statdefs[i].mode = stats[i].mode;
1365 statdefs[i].percent = stats[i].percent;
1366 statdefs[i].scale = stats[i].scale;
1367 statdefs[i].multiplier = stats[i].multiplier;
1368 statdefs[i].divisor = stats[i].divisor;
1369 }
1370 for (estatdefscount=0 ; estats[estatdefscount].divisor ; estatdefscount++) {}
1371 if (estatdefscount>0) {
1372 estatdefs = (estatdef*)malloc(sizeof(estatdef)*estatdefscount);
1373 passert(estatdefs);
1374 } else {
1375 estatdefs = NULL;
1376 }
1377 for (i=0 ; i<estatdefscount ; i++) {
1378 if (estats[i].name!=NULL) {
1379 estatdefs[i].name = strdup(estats[i].name);
1380 passert(estatdefs[i].name);
1381 } else {
1382 estatdefs[i].name = NULL;
1383 }
1384 estatdefs[i].statid = estats[i].statid;
1385 estatdefs[i].c1src = estats[i].c1src;
1386 estatdefs[i].c2src = estats[i].c2src;
1387 estatdefs[i].c3src = estats[i].c3src;
1388 estatdefs[i].mode = estats[i].mode;
1389 estatdefs[i].percent = estats[i].percent;
1390 estatdefs[i].scale = estats[i].scale;
1391 estatdefs[i].multiplier = estats[i].multiplier;
1392 estatdefs[i].divisor = estats[i].divisor;
1393 }
1394
1395 if (statdefscount>0) {
1396 monotonic = (uint64_t*)malloc(sizeof(uint64_t)*statdefscount);
1397 passert(monotonic);
1398 series = (stat_record*)malloc(sizeof(stat_record)*statdefscount);
1399 passert(series);
1400 for (i=0 ; i<statdefscount ; i++) {
1401 monotonic[i] = 0;
1402 for (j=0 ; j<RANGES ; j++) {
1403 series[i][j] = malloc(MAXLENG*sizeof(uint64_t));
1404 passert(series[i][j]);
1405 memset(series[i][j],0xFF,MAXLENG*sizeof(uint64_t));
1406 }
1407 }
1408 } else {
1409 series = NULL;
1410 monotonic = NULL;
1411 }
1412
1413 for (i=0 ; i<RANGES ; i++) {
1414 pointers[i]=0;
1415 timepoint[i]=0;
1416 }
1417
1418 if (charts_load(mode)<0) {
1419 return -1;
1420 }
1421 charts_inittimepointers();
1422 if (mode==0) {
1423 charts_add(NULL,time(NULL));
1424 }
1425
1426 #ifdef HAVE_ZLIB_H
1427 zstr.zalloc = NULL;
1428 zstr.zfree = NULL;
1429 zstr.opaque = NULL;
1430 if (deflateInit(&zstr,Z_BEST_SPEED)!=Z_OK) {
1431 //if (deflateInit(&zstr,Z_DEFAULT_COMPRESSION)!=Z_OK) {
1432 return -1;
1433 }
1434 #endif /* HAVE_ZLIB_H */
1435 png_make_palette();
1436 return 0;
1437 }
1438
1439 #ifndef HAVE_ZLIB_H
charts_putwarning(uint32_t posx,uint32_t posy,uint8_t color)1440 static inline void charts_putwarning(uint32_t posx,uint32_t posy,uint8_t color) {
1441 uint8_t *w,c,fx,fy,b;
1442 uint32_t x,y;
1443 w = warning;
1444 for (fy=0 ; fy<11 ; fy++) {
1445 y = fy+posy;
1446 for (fx=0 ; fx<86 ; fx++) {
1447 x = fx+posx;
1448 if (x<MAXXSIZE && y<MAXYSIZE) {
1449 chart[(MAXXSIZE)*y+x] = (fy==0||fy==10||fx==0||fx==85)?COLOR_AXIS:COLOR_TEXTBKG;
1450 }
1451 }
1452 }
1453 y = posy+3;
1454 for (fy=0 ; fy<5 ; fy++) {
1455 x = posx+3;
1456 for (b=0 ; b<10 ; b++) {
1457 c = *w;
1458 w++;
1459 for (fx=0 ; fx<8 ; fx++) {
1460 if (c&0x80 && x<MAXXSIZE && y<MAXYSIZE) {
1461 chart[(MAXXSIZE)*y+x] = color;
1462 }
1463 c<<=1;
1464 x++;
1465 }
1466 }
1467 y++;
1468 }
1469 }
1470 #endif
1471
charts_puttext(int32_t posx,int32_t posy,uint8_t color,uint8_t * data,uint32_t leng,int32_t minx,int32_t maxx,int32_t miny,int32_t maxy)1472 static inline void charts_puttext(int32_t posx,int32_t posy,uint8_t color,uint8_t *data,uint32_t leng,int32_t minx,int32_t maxx,int32_t miny,int32_t maxy) {
1473 uint32_t i,fx,fy;
1474 uint8_t fp,fbits;
1475 int32_t px,x,y;
1476 for (i=0 ; i<leng ; i++) {
1477 px = i*6+posx;
1478 fp = data[i];
1479 if (fp>SQUARE) {
1480 fp=SQUARE;
1481 }
1482 for (fy=0 ; fy<9 ; fy++) {
1483 fbits = font[fp][fy];
1484 if (fbits) {
1485 for (fx=0 ; fx<5 ; fx++) {
1486 x = px+fx;
1487 y = posy+fy;
1488 if (fbits&0x10 && x>=minx && x<=maxx && y>=miny && y<=maxy) {
1489 chart[(MAXXSIZE)*y+x] = color;
1490 }
1491 fbits<<=1;
1492 }
1493 }
1494 }
1495 }
1496 }
1497
charts_fixmax(uint64_t max,uint32_t ypts,uint8_t * scale,uint8_t * mode,uint16_t * base)1498 double charts_fixmax(uint64_t max,uint32_t ypts,uint8_t *scale,uint8_t *mode,uint16_t *base) {
1499 uint64_t cpmax,factor;
1500 uint8_t cmode,ascale;
1501
1502 if (max==0) {
1503 max=1;
1504 }
1505 if (max<=9) {
1506 (*base) = ((max*100)+ypts-1)/ypts;
1507 if (((*base)*ypts)<1000) {
1508 (*mode) = 2;
1509 return ((*base)*ypts)/100;
1510 }
1511 }
1512 if (max<=99) {
1513 (*base) = ((max*10)+ypts-1)/ypts;
1514 if (((*base)*ypts)<1000) {
1515 (*mode) = 1;
1516 return ((*base)*ypts)/10;
1517 }
1518 }
1519 cpmax = 999;
1520 cmode = 0;
1521 ascale = 0;
1522 factor = ypts;
1523 while (1) {
1524 if (max<=cpmax) {
1525 (*base) = (max+factor-1)/factor;
1526 if (((*base)*ypts)<1000) {
1527 (*mode) = cmode;
1528 (*scale) += ascale;
1529 return ((*base)*factor);
1530 }
1531 }
1532 if (cmode==0) {
1533 cmode=2;
1534 ascale+=1;
1535 } else {
1536 cmode--;
1537 }
1538 factor *= 10U;
1539 if (cpmax*10U > cpmax) {
1540 cpmax *= 10U;
1541 } else {
1542 if (max+9<max) {
1543 (*base) = (max/10+factor/10-1)/(factor/10);
1544 } else {
1545 (*base) = ((max+9)/10+factor/10-1)/(factor/10);
1546 }
1547 (*mode) = cmode;
1548 (*scale) += ascale;
1549 return ((double)(*base)*(double)factor);
1550 }
1551 }
1552 }
1553
charts_makechart(uint32_t type,uint32_t range,uint32_t width,uint32_t height)1554 void charts_makechart(uint32_t type,uint32_t range,uint32_t width,uint32_t height) {
1555 static const uint8_t jtab[11]={MICRO,MILI,SPACE,KILO,MEGA,GIGA,TERA,PETA,EXA,ZETTA,YOTTA};
1556 int32_t i,j;
1557 uint32_t xy,xm,xd,xh,xs,xoff,xbold,ys,ypts;
1558 uint64_t max;
1559 double dmax;
1560 uint64_t d,c1d,c2d,c3d;
1561 uint64_t c1dispdata[MAXLENG];
1562 uint64_t c2dispdata[MAXLENG];
1563 uint64_t c3dispdata[MAXLENG];
1564 uint8_t scale,mode=0,percent=0;
1565 uint16_t base=0;
1566 uint8_t text[6];
1567 uint8_t colors;
1568
1569 memset(chart,COLOR_TRANSPARENT,(MAXXSIZE)*(MAXYSIZE));
1570
1571 colors = 0;
1572 if (charts_filltab(c1dispdata,range,type,1,width)) {
1573 colors = 1;
1574 }
1575 if (charts_filltab(c2dispdata,range,type,2,width)) {
1576 colors = 2;
1577 }
1578 if (charts_filltab(c3dispdata,range,type,3,width)) {
1579 colors = 3;
1580 }
1581
1582 max = 0;
1583 for (i=0 ; i<(int32_t)width ; i++) {
1584 d = 0;
1585 if (colors>=1 && c1dispdata[i]!=CHARTS_NODATA) {
1586 d += c1dispdata[i];
1587 }
1588 if (colors>=2 && c2dispdata[i]!=CHARTS_NODATA) {
1589 d += c2dispdata[i];
1590 }
1591 if (colors>=3 && c3dispdata[i]!=CHARTS_NODATA) {
1592 d += c3dispdata[i];
1593 }
1594 if (d>max) {
1595 max=d;
1596 }
1597 }
1598 if (max>1000000000000000000ULL) { // arithmetic overflow protection
1599 for (i=0 ; i<(int32_t)width ; i++) {
1600 if (colors>=1 && c1dispdata[i]!=CHARTS_NODATA) {
1601 c1dispdata[i]/=1000;
1602 }
1603 if (colors>=2 && c2dispdata[i]!=CHARTS_NODATA) {
1604 c2dispdata[i]/=1000;
1605 }
1606 if (colors>=3 && c3dispdata[i]!=CHARTS_NODATA) {
1607 c3dispdata[i]/=1000;
1608 }
1609 }
1610 max/=1000;
1611 scale=1;
1612 } else {
1613 scale=0;
1614 }
1615
1616 // range scale
1617 if ((CHARTS_IS_DIRECT_STAT(type) && statdefs[type].mode==CHARTS_MODE_ADD) || (CHARTS_IS_EXTENDED_STAT(type) && estatdefs[CHARTS_EXTENDED_POS(type)].mode==CHARTS_MODE_ADD)) {
1618 switch (range) {
1619 case MEDIUMRANGE:
1620 max = (max+5)/6;
1621 break;
1622 case LONGRANGE:
1623 max = (max+29)/30;
1624 break;
1625 case VERYLONGRANGE:
1626 max = (max+1439)/(24*60);
1627 break;
1628 }
1629 }
1630
1631 if (CHARTS_IS_DIRECT_STAT(type)) {
1632 scale += statdefs[type].scale;
1633 percent = statdefs[type].percent;
1634 max *= statdefs[type].multiplier;
1635 max /= statdefs[type].divisor;
1636 } else if (CHARTS_IS_EXTENDED_STAT(type)) {
1637 scale += estatdefs[CHARTS_EXTENDED_POS(type)].scale;
1638 percent = estatdefs[CHARTS_EXTENDED_POS(type)].percent;
1639 max *= estatdefs[CHARTS_EXTENDED_POS(type)].multiplier;
1640 max /= estatdefs[CHARTS_EXTENDED_POS(type)].divisor;
1641 }
1642
1643 ypts = height/20;
1644 dmax = charts_fixmax(max,ypts,&scale,&mode,&base);
1645
1646 if (CHARTS_IS_DIRECT_STAT(type)) {
1647 dmax *= statdefs[type].divisor;
1648 dmax /= statdefs[type].multiplier;
1649 } else if (CHARTS_IS_EXTENDED_STAT(type)) {
1650 dmax *= estatdefs[CHARTS_EXTENDED_POS(type)].divisor;
1651 dmax /= estatdefs[CHARTS_EXTENDED_POS(type)].multiplier;
1652 }
1653
1654 // range scale
1655 if ((CHARTS_IS_DIRECT_STAT(type) && statdefs[type].mode==CHARTS_MODE_ADD) || (CHARTS_IS_EXTENDED_STAT(type) && estatdefs[CHARTS_EXTENDED_POS(type)].mode==CHARTS_MODE_ADD)) {
1656 switch (range) {
1657 case MEDIUMRANGE:
1658 dmax *= 6;
1659 break;
1660 case LONGRANGE:
1661 dmax *= 30;
1662 break;
1663 case VERYLONGRANGE:
1664 dmax *= (24*60);
1665 break;
1666 }
1667 }
1668
1669 // m = 0;
1670 for (i=0 ; i<(int32_t)width ; i++) {
1671 j = 0;
1672 if (colors>=3 && c3dispdata[i]!=CHARTS_NODATA) {
1673 c3d = c3dispdata[i];
1674 j = 1;
1675 } else {
1676 c3d = 0;
1677 }
1678 if (colors>=2 && c2dispdata[i]!=CHARTS_NODATA) {
1679 c2d = c3d + c2dispdata[i];
1680 j = 1;
1681 } else {
1682 c2d = c3d;
1683 }
1684 if (colors>=1 && c1dispdata[i]!=CHARTS_NODATA) {
1685 c1d = c2d + c1dispdata[i];
1686 j = 1;
1687 } else {
1688 c1d = c2d;
1689 }
1690
1691 if (j==0) {
1692 for (j=0 ; j<(int32_t)height ; j++) {
1693 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = ((j+i)%3)?COLOR_BKG:COLOR_NODATA; //(((j+i)&3)&&((j+2+LENG-i)&3))?COLOR_BKG:COLOR_DATA1;
1694 }
1695 } else {
1696 if (c1d>281474976710656ULL) { // arithmetic overflow protection
1697 c1d /= 65536;
1698 c2d /= 65536;
1699 c3d /= 65536;
1700 c1d *= height;
1701 c1d /= (dmax/65536);
1702 c2d *= height;
1703 c2d /= (dmax/65536);
1704 c3d *= height;
1705 c3d /= (dmax/65536);
1706 } else {
1707 c1d *= height;
1708 c1d /= dmax;
1709 c2d *= height;
1710 c2d /= dmax;
1711 c3d *= height;
1712 c3d /= dmax;
1713 }
1714
1715 j=0;
1716 while (height>=c1d+j) {
1717 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_BKG;
1718 j++;
1719 }
1720 while (height>=c2d+j) {
1721 #if VERSMAJ==1
1722 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA1;
1723 #else
1724 if (colors==1) {
1725 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + (height-1-j)*3*COLOR_DATA_RANGE/height;
1726 } else if (colors==2) {
1727 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + (height-1-j)*1.5*COLOR_DATA_RANGE/height;
1728 } else if (colors==3) {
1729 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + (height-1-j)*COLOR_DATA_RANGE/height;
1730 }
1731 #endif
1732 j++;
1733 }
1734 while (height>=c3d+j) {
1735 #if VERSMAJ==1
1736 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA2;
1737 #else
1738 if (colors==2) {
1739 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + 1.5*COLOR_DATA_RANGE + (height-1-j)*1.5*COLOR_DATA_RANGE/height;
1740 } else if (colors==3) {
1741 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + COLOR_DATA_RANGE + (height-1-j)*COLOR_DATA_RANGE/height;
1742 }
1743 #endif
1744 j++;
1745 }
1746 while ((int32_t)height>j) {
1747 #if VERSMAJ==1
1748 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA3;
1749 #else
1750 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_DATA_BEGIN + 2*COLOR_DATA_RANGE + (height-1-j)*COLOR_DATA_RANGE/height;
1751 #endif
1752 j++;
1753 }
1754 }
1755 }
1756 // axes
1757 for (i=-3 ; i<(int32_t)width+3 ; i++) {
1758 chart[(MAXXSIZE)*(height+YPOS)+(i+XPOS)] = COLOR_AXIS;
1759 }
1760 for (i=-2 ; i<(int32_t)height+5 ; i++) {
1761 chart[(MAXXSIZE)*(height-i+YPOS)+(XPOS-1)] = COLOR_AXIS;
1762 chart[(MAXXSIZE)*(height-i+YPOS)+(XPOS+width)] = COLOR_AXIS;
1763 }
1764
1765 // x scale
1766 xy = xm = xd = xh = xs = 0;
1767 if (range<3) {
1768 if (range==2) {
1769 xs = 12;
1770 xoff = lnghalfhour%12;
1771 xbold = 4;
1772 xh = lnghalfhour/12;
1773 xd = lngmday;
1774 xm = lngmonth;
1775 xy = lngyear;
1776 } else if (range==1) {
1777 xs = 10;
1778 xoff = medmin/6;
1779 xbold = 6;
1780 xh = medhour;
1781 } else {
1782 xs = 60;
1783 xoff = shmin;
1784 xbold = 1;
1785 xh = shhour;
1786 }
1787 // k = MAXLENG;
1788 for (i=width-xoff-1 ; i>=0 ; i-=xs) {
1789 if (xh%xbold==0) {
1790 ys=2;
1791 if ((range==0 && xh%6==0) || (range==1 && xh==0) || (range==2 && xd==1)) {
1792 ys=1;
1793 }
1794 if (range<2) {
1795 text[0]=xh/10;
1796 text[1]=xh%10;
1797 text[2]=COLON;
1798 text[3]=0;
1799 text[4]=0;
1800 charts_puttext(XPOS+i-14,(YPOS+height)+4,COLOR_TEXT,text,5,XPOS,XPOS+width-1,0,MAXYSIZE-1);
1801 } else {
1802 text[0]=xm/10;
1803 text[1]=xm%10;
1804 text[2]=FDOT;
1805 text[3]=xd/10;
1806 text[4]=xd%10;
1807 charts_puttext(XPOS+i+10,(YPOS+height)+4,COLOR_TEXT,text,5,XPOS,XPOS+width-1,0,MAXYSIZE-1);
1808 xd--;
1809 if (xd==0) {
1810 xm--;
1811 if (xm==0) {
1812 xm=12;
1813 xy--;
1814 }
1815 xd = getmonleng(xy,xm);
1816 }
1817 }
1818 chart[(MAXXSIZE)*(YPOS+height+1)+(i+XPOS)] = COLOR_AXIS;
1819 chart[(MAXXSIZE)*(YPOS+height+2)+(i+XPOS)] = COLOR_AXIS;
1820 } else {
1821 ys=4;
1822 }
1823 for (j=0 ; j<(int32_t)height ; j+=ys) {
1824 if (ys>1 || (j%4)!=0) {
1825 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_AUX;
1826 }
1827 }
1828 if (range<2) {
1829 if (xh==0) {
1830 xh=23;
1831 } else {
1832 xh--;
1833 }
1834 } else {
1835 if (xh==0) {
1836 xh=3;
1837 } else {
1838 xh--;
1839 }
1840 }
1841 }
1842 if (range==2) {
1843 i -= xs*xh;
1844 text[0]=xm/10;
1845 text[1]=xm%10;
1846 text[2]=FDOT;
1847 text[3]=xd/10;
1848 text[4]=xd%10;
1849 charts_puttext(XPOS+i+10,(YPOS+height)+4,COLOR_TEXT,text,5,XPOS,XPOS+width-1,0,MAXYSIZE-1);
1850 }
1851 } else {
1852 xy = lngyear;
1853 xm = lngmonth;
1854 // k = MAXLENG;
1855 for (i=width-lngmday ; i>=0 ; ) {
1856 text[0]=xm/10;
1857 text[1]=xm%10;
1858 charts_puttext(XPOS+i+(getmonleng(xy,xm)-11)/2+1,(YPOS+height)+4,COLOR_TEXT,text,2,XPOS,XPOS+width-1,0,MAXYSIZE-1);
1859 chart[(MAXXSIZE)*(YPOS+height+1)+(i+XPOS)] = COLOR_AXIS;
1860 chart[(MAXXSIZE)*(YPOS+height+2)+(i+XPOS)] = COLOR_AXIS;
1861 if (xm!=1) {
1862 for (j=0 ; j<(int32_t)height ; j+=2) {
1863 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_AUX;
1864 }
1865 } else {
1866 for (j=0 ; j<(int32_t)height ; j++) {
1867 if ((j%4)!=0) {
1868 chart[(MAXXSIZE)*(j+YPOS)+(i+XPOS)] = COLOR_AUX;
1869 }
1870 }
1871 }
1872 xm--;
1873 if (xm==0) {
1874 xm=12;
1875 xy--;
1876 }
1877 i-=getmonleng(xy,xm);
1878 // k = i;
1879 }
1880 text[0]=xm/10;
1881 text[1]=xm%10;
1882 charts_puttext(XPOS+i+(getmonleng(xy,xm)-11)/2+1,(YPOS+height)+4,COLOR_TEXT,text,2,XPOS,XPOS+width-1,0,MAXYSIZE-1);
1883 }
1884 // y scale
1885
1886 /*
1887 // range scale
1888 if ((CHARTS_IS_DIRECT_STAT(type) && statdefs[type].mode==CHARTS_MODE_ADD) || (CHARTS_IS_EXTENDED_STAT(type) && estatdefs[CHARTS_EXTENDED_POS(type)].mode==CHARTS_MODE_ADD)) {
1889 switch (range) {
1890 case SHORTRANGE:
1891 ymax = max;
1892 break;
1893 case MEDIUMRANGE:
1894 ymax = max/6;
1895 break;
1896 case LONGRANGE:
1897 ymax = max/30;
1898 break;
1899 case VERYLONGRANGE:
1900 ymax = max/(24*60);
1901 break;
1902 default:
1903 ymax=0;
1904 }
1905 } else {
1906 ymax = max;
1907 }
1908
1909 if (CHARTS_IS_DIRECT_STAT(type)) {
1910 scale = statdefs[type].scale;
1911 ymax *= statdefs[type].multiplier;
1912 // ymin *= statdefs[type].multiplier;
1913 ymax /= statdefs[type].divisor;
1914 // ymin /= statdefs[type].divisor;
1915 } else if (CHARTS_IS_EXTENDED_STAT(type)) {
1916 scale = estatdefs[CHARTS_EXTENDED_POS(type)].scale;
1917 ymax *= estatdefs[CHARTS_EXTENDED_POS(type)].multiplier;
1918 // ymin *= estatdefs[CHARTS_EXTENDED_POS(type)].multiplier;
1919 ymax /= estatdefs[CHARTS_EXTENDED_POS(type)].divisor;
1920 // ymin /= estatdefs[CHARTS_EXTENDED_POS(type)].divisor;
1921 }
1922 */
1923 // for (i=0 ; i<LENG ; i+=2) {
1924 // for (j=DATA-20 ; j>=0 ; j-=20) {
1925 // chart[(XSIZE)*(j+YPOS)+(i+XPOS)] = 2;
1926 // }
1927 // }
1928 for (i=0 ; i<=(int32_t)ypts ; i++) {
1929 d = base*i;
1930 j=0;
1931 if (mode==0) { // ###
1932 if (d>=10) {
1933 if (d>=100) {
1934 text[j++]=d/100;
1935 d%=100;
1936 }
1937 text[j++]=d/10;
1938 }
1939 text[j++]=d%10;
1940 } else if (mode==1) { // ##.#
1941 if (d>=100) {
1942 text[j++]=d/100;
1943 d%=100;
1944 }
1945 text[j++]=d/10;
1946 text[j++]=FDOT;
1947 text[j++]=d%10;
1948 } else if (mode==2) { // #.##
1949 text[j++]=d/100;
1950 d%=100;
1951 text[j++]=FDOT;
1952 text[j++]=d/10;
1953 text[j++]=d%10;
1954 }
1955 if (scale<11) {
1956 if (jtab[scale]!=SPACE) {
1957 text[j++]=jtab[scale];
1958 }
1959 } else {
1960 text[j++]=SQUARE;
1961 }
1962 if (percent) {
1963 text[j++]=PERCENT;
1964 }
1965 charts_puttext(XPOS - 4 - (j*6),(YPOS+height-(20*i))-3,COLOR_TEXT,text,j,0,MAXXSIZE-1,0,MAXYSIZE-1);
1966 chart[(MAXXSIZE)*(YPOS+height-20*i)+(XPOS-2)] = COLOR_AXIS;
1967 chart[(MAXXSIZE)*(YPOS+height-20*i)+(XPOS-3)] = COLOR_AXIS;
1968 if (i>0) {
1969 for (j=1 ; j<(int32_t)width ; j+=2) {
1970 chart[(MAXXSIZE)*(YPOS+height-20*i)+(XPOS+j)] = COLOR_AUX;
1971 }
1972 }
1973 }
1974 }
1975
charts_monotonic_data(uint8_t * buff)1976 uint32_t charts_monotonic_data (uint8_t *buff) {
1977 uint32_t i;
1978 if (buff==NULL) {
1979 return sizeof(uint16_t)+sizeof(uint64_t)*statdefscount;
1980 }
1981 put16bit(&buff,statdefscount);
1982 for (i=0 ; i<statdefscount ; i++) {
1983 put64bit(&buff,monotonic[i]);
1984 }
1985 return 0;
1986 }
1987
charts_statid_converter(uint32_t number,uint32_t * chtype,uint32_t * chrange)1988 static inline void charts_statid_converter(uint32_t number,uint32_t *chtype,uint32_t *chrange) {
1989 uint32_t rmask;
1990 uint32_t i;
1991 if (number < 0x1000000) {
1992 *chtype = number / 10;
1993 *chrange = number % 10;
1994 return;
1995 } else {
1996 rmask = number & 0x20202020;
1997 rmask = ((rmask>>5)&1)|((rmask>>12)&2)|((rmask>>19)&4)|((rmask>>26)&8);
1998 number &= 0xDFDFDFDF;
1999 for (i=0 ; i<statdefscount ; i++) {
2000 if ((statdefs[i].statid&0xDFDFDFDF)==number) {
2001 *chtype = i;
2002 *chrange = rmask;
2003 return;
2004 }
2005 }
2006 for (i=0 ; i<estatdefscount ; i++) {
2007 if ((estatdefs[i].statid&0xDFDFDFDF)==number) {
2008 *chtype = CHARTS_EXTENDED_START+i;
2009 *chrange = rmask;
2010 return;
2011 }
2012 }
2013 }
2014 *chtype = 0xFFFFFFFF;
2015 *chrange = 0xFFFFFFFF;
2016 return;
2017 }
2018
charts_data_multiplier(uint32_t type,uint32_t range)2019 double charts_data_multiplier(uint32_t type,uint32_t range) {
2020 double ret;
2021 ret = 1.0;
2022 if ((CHARTS_IS_DIRECT_STAT(type) && statdefs[type].mode==CHARTS_MODE_ADD) || (CHARTS_IS_EXTENDED_STAT(type) && estatdefs[CHARTS_EXTENDED_POS(type)].mode==CHARTS_MODE_ADD)) {
2023 switch (range) {
2024 case MEDIUMRANGE:
2025 ret /= 6.0;
2026 break;
2027 case LONGRANGE:
2028 ret /= 30.0;
2029 break;
2030 case VERYLONGRANGE:
2031 ret /= 1440.0;
2032 break;
2033 }
2034 }
2035 if (CHARTS_IS_DIRECT_STAT(type)) {
2036 ret *= statdefs[type].multiplier;
2037 ret /= statdefs[type].divisor;
2038 switch (statdefs[type].scale) {
2039 case CHARTS_SCALE_MICRO:
2040 ret /= 1000000.0;
2041 break;
2042 case CHARTS_SCALE_MILI:
2043 ret /= 1000.0;
2044 break;
2045 case CHARTS_SCALE_KILO:
2046 ret *= 1000.0;
2047 break;
2048 case CHARTS_SCALE_MEGA:
2049 ret *= 1000000.0;
2050 break;
2051 case CHARTS_SCALE_GIGA:
2052 ret *= 1000000000.0;
2053 break;
2054 }
2055 } else if (CHARTS_IS_EXTENDED_STAT(type)) {
2056 ret *= estatdefs[CHARTS_EXTENDED_POS(type)].multiplier;
2057 ret /= estatdefs[CHARTS_EXTENDED_POS(type)].divisor;
2058 switch (estatdefs[CHARTS_EXTENDED_POS(type)].scale) {
2059 case CHARTS_SCALE_MICRO:
2060 ret /= 1000000.0;
2061 break;
2062 case CHARTS_SCALE_MILI:
2063 ret /= 1000.0;
2064 break;
2065 case CHARTS_SCALE_KILO:
2066 ret *= 1000.0;
2067 break;
2068 case CHARTS_SCALE_MEGA:
2069 ret *= 1000000.0;
2070 break;
2071 case CHARTS_SCALE_GIGA:
2072 ret *= 1000000000.0;
2073 break;
2074 }
2075 }
2076 return ret;
2077 }
2078
charts_getmaxleng(void)2079 uint32_t charts_getmaxleng(void) {
2080 return MAXLENG;
2081 }
2082
charts_getdata(double * data,uint32_t * timestamp,uint32_t * rsec,uint32_t number)2083 void charts_getdata(double *data,uint32_t *timestamp,uint32_t *rsec,uint32_t number) {
2084 uint32_t i,chtype,chrange;
2085 uint64_t tab1[MAXLENG];
2086 uint64_t tab2[MAXLENG];
2087 uint64_t tab3[MAXLENG];
2088 uint64_t d;
2089 uint32_t c;
2090 uint8_t nd;
2091 double mul;
2092
2093 charts_statid_converter(number,&chtype,&chrange);
2094 if (data!=NULL && timestamp!=NULL && chrange<RANGES && (CHARTS_IS_DIRECT_STAT(chtype) || CHARTS_IS_EXTENDED_STAT(chtype))) {
2095 #ifdef USE_PTHREADS
2096 zassert(pthread_mutex_lock(&glock));
2097 #endif
2098 mul = charts_data_multiplier(chtype,chrange);
2099 c = 0;
2100 if (charts_filltab(tab1,chrange,chtype,1,MAXLENG)) {
2101 c = 1;
2102 }
2103 if (charts_filltab(tab2,chrange,chtype,2,MAXLENG)) {
2104 c = 2;
2105 }
2106 if (charts_filltab(tab3,chrange,chtype,3,MAXLENG)) {
2107 c = 3;
2108 }
2109 switch (chrange) {
2110 case SHORTRANGE:
2111 *rsec = 60;
2112 break;
2113 case MEDIUMRANGE:
2114 *rsec = 60*6;
2115 break;
2116 case LONGRANGE:
2117 *rsec = 60*30;
2118 break;
2119 case VERYLONGRANGE:
2120 *rsec = 60*60*24;
2121 break;
2122 }
2123 *timestamp = timepoint[chrange] * (*rsec);
2124 for (i=0 ; i<MAXLENG ; i++) {
2125 d = 0;
2126 nd = 1;
2127 if (c>=1 && tab1[i]!=CHARTS_NODATA) {
2128 d += tab1[i];
2129 nd = 0;
2130 }
2131 if (c>=2 && tab2[i]!=CHARTS_NODATA) {
2132 d += tab2[i];
2133 nd = 0;
2134 }
2135 if (c>=3 && tab3[i]!=CHARTS_NODATA) {
2136 d += tab3[i];
2137 nd = 0;
2138 }
2139 if (nd) {
2140 data[i] = -1.0;
2141 } else {
2142 data[i] = d * mul;
2143 }
2144 }
2145 #ifdef USE_PTHREADS
2146 zassert(pthread_mutex_unlock(&glock));
2147 #endif
2148 }
2149 }
2150
charts_makedata(uint8_t * buff,uint32_t number,uint32_t maxentries)2151 uint32_t charts_makedata(uint8_t *buff,uint32_t number,uint32_t maxentries) {
2152 uint32_t i,j,ts,chtype,chrange;
2153 uint64_t *tab;
2154
2155 charts_statid_converter(number,&chtype,&chrange);
2156 if (maxentries>MAXLENG) {
2157 maxentries = MAXLENG;
2158 }
2159 if (buff==NULL) {
2160 return (chrange<RANGES && CHARTS_IS_DIRECT_STAT(chtype))?maxentries*8+8:0;
2161 }
2162 if (chrange<RANGES && CHARTS_IS_DIRECT_STAT(chtype)) {
2163 #ifdef USE_PTHREADS
2164 zassert(pthread_mutex_lock(&glock));
2165 #endif
2166 tab = series[chtype][chrange];
2167 j = pointers[chrange] % MAXLENG;
2168 ts = timepoint[chrange];
2169 switch (chrange) {
2170 case SHORTRANGE:
2171 ts *= 60;
2172 break;
2173 case MEDIUMRANGE:
2174 ts *= 60*6;
2175 break;
2176 case LONGRANGE:
2177 ts *= 60*30;
2178 break;
2179 case VERYLONGRANGE:
2180 ts *= 60*60*24;
2181 break;
2182 }
2183 put32bit(&buff,ts);
2184 put32bit(&buff,maxentries);
2185 for (i=0 ; i<maxentries ; i++) {
2186 put64bit(&buff,tab[j]);
2187 if (j>0) {
2188 j--;
2189 } else {
2190 j = MAXLENG-1;
2191 }
2192 }
2193 #ifdef USE_PTHREADS
2194 zassert(pthread_mutex_unlock(&glock));
2195 #endif
2196 }
2197 return 0;
2198 }
2199
charts_chart_to_rawchart(uint32_t chartwidth,uint32_t chartheight)2200 void charts_chart_to_rawchart(uint32_t chartwidth,uint32_t chartheight) {
2201 uint32_t y;
2202 // uint32_t x;
2203 uint8_t *cp,*rp;
2204 cp = chart;
2205 rp = rawchart;
2206 for (y=0 ; y<chartheight ; y++) {
2207 *rp=0;
2208 rp++;
2209 memcpy(rp,cp,chartwidth);
2210 rp+=chartwidth;
2211 cp+=MAXXSIZE;
2212 }
2213 }
2214
charts_fill_crc(uint8_t * buff,uint32_t leng)2215 void charts_fill_crc(uint8_t *buff,uint32_t leng) {
2216 uint8_t *ptr,*eptr;
2217 uint32_t crc,chleng;
2218 ptr = buff+8;
2219 eptr = buff+leng;
2220 while (ptr+4<=eptr) {
2221 chleng = get32bit((const uint8_t **)&ptr);
2222 if (ptr+8+chleng<=eptr) {
2223 crc = mycrc32(0,ptr,chleng+4);
2224 ptr += chleng+4;
2225 if (memcmp(ptr,"CRC#",4)==0) {
2226 put32bit(&ptr,crc);
2227 } else {
2228 syslog(LOG_WARNING,"charts: unexpected data in generated png stream");
2229 }
2230 }
2231 }
2232 }
2233
2234 #ifndef HAVE_ZLIB_H
2235
2236 #define MOD_ADLER 65521
2237
charts_adler32(uint8_t * data,uint32_t len)2238 static uint32_t charts_adler32(uint8_t *data,uint32_t len) {
2239 uint32_t a = 1, b = 0;
2240 uint32_t i;
2241
2242 for (i=0 ; i<len ; i++) {
2243 a = (a + data[i]) % MOD_ADLER;
2244 b = (b + a) % MOD_ADLER;
2245 }
2246
2247 return (b << 16) | a;
2248 }
2249
charts_fake_compress(uint8_t * src,uint32_t srcsize,uint8_t * dst,uint32_t * dstsize)2250 int charts_fake_compress(uint8_t *src,uint32_t srcsize,uint8_t *dst,uint32_t *dstsize) {
2251 uint32_t edstsize,adler;
2252 edstsize = 6+(65535+5)*(srcsize/65535);
2253 if (srcsize%65535) {
2254 edstsize+=5+(srcsize%65535);
2255 }
2256 if (edstsize>*dstsize) {
2257 return -1;
2258 }
2259 adler = charts_adler32(src,srcsize);
2260 *dst++=0x78;
2261 *dst++=0x9C;
2262 while (srcsize>65535) {
2263 *dst++ = 0x00;
2264 *dst++ = 0xFF;
2265 *dst++ = 0xFF;
2266 *dst++ = 0x00;
2267 *dst++ = 0x00;
2268 memcpy(dst,src,65535);
2269 dst+=65535;
2270 src+=65535;
2271 srcsize-=65535;
2272 }
2273 if (srcsize>0) {
2274 *dst++ = 0x01;
2275 *dst++ = srcsize&0xFF;
2276 *dst++ = srcsize>>8;
2277 *dst++ = (srcsize&0xFF)^0xFF;
2278 *dst++ = (srcsize>>8)^0xFF;
2279 memcpy(dst,src,srcsize);
2280 dst+=srcsize;
2281 }
2282 *dst++ = (adler>>24) & 0xFF;
2283 *dst++ = (adler>>16) & 0xFF;
2284 *dst++ = (adler>>8) & 0xFF;
2285 *dst++ = adler & 0xFF;
2286 *dstsize = edstsize;
2287 return 0;
2288 }
2289 #endif /* ! HAVE_ZLIB_H */
2290
charts_make_png(uint32_t number,uint32_t chartwidth,uint32_t chartheight)2291 uint32_t charts_make_png(uint32_t number,uint32_t chartwidth,uint32_t chartheight) {
2292 uint32_t chtype,chrange;
2293 uint8_t *ptr;
2294
2295 #ifdef USE_PTHREADS
2296 zassert(pthread_mutex_lock(&glock));
2297 #endif
2298 charts_statid_converter(number,&chtype,&chrange);
2299 if (chrange>=RANGES) {
2300 compsize = 0;
2301 return sizeof(png_1x1);
2302 }
2303 if (!(CHARTS_IS_DIRECT_STAT(chtype) || CHARTS_IS_EXTENDED_STAT(chtype))) {
2304 compsize = 0;
2305 return sizeof(png_1x1);
2306 }
2307
2308 if (chartwidth==0 && chartheight==0) {
2309 #if VERSMAJ==1
2310 chartwidth = 1000;
2311 #else
2312 chartwidth = 950+XADD;
2313 #endif
2314 chartheight = 100+YADD;
2315 }
2316 if (chartheight>MAXHEIGHT+YADD) {
2317 chartheight = MAXHEIGHT+YADD;
2318 }
2319 if (chartheight<MINHEIGHT+YADD) {
2320 chartheight = MINHEIGHT+YADD;
2321 }
2322 if (((chartheight-YADD) % 20)!=0) {
2323 chartheight -= ((chartheight-YADD) % 20);
2324 }
2325 if (chartwidth>MAXLENG+XADD) {
2326 chartwidth = MAXLENG+XADD;
2327 }
2328 if (chartwidth<MINLENG+XADD) {
2329 chartwidth = MINLENG+XADD;
2330 }
2331
2332 charts_makechart(chtype,chrange,chartwidth-XADD,chartheight-YADD);
2333 #ifndef HAVE_ZLIB_H
2334 charts_putwarning(XPOS+chartwidth-XADD-90,YPOS+5,COLOR_AXIS);
2335 #endif
2336 charts_chart_to_rawchart(chartwidth,chartheight);
2337
2338 ptr = png_header+16;
2339 put32bit(&ptr,chartwidth);
2340 put32bit(&ptr,chartheight);
2341
2342 #ifdef HAVE_ZLIB_H
2343 if (deflateReset(&zstr)!=Z_OK) {
2344 compsize = 0;
2345 return sizeof(png_1x1);
2346 }
2347
2348 // syslog(LOG_NOTICE,"rawchartsize: %u ; compbuffsize: %u",rawchartsize,compbuffsize);
2349 zstr.next_in = rawchart;
2350 zstr.avail_in = rawchartsize;
2351 zstr.total_in = 0;
2352 zstr.next_out = compbuff;
2353 zstr.avail_out = compbuffsize;
2354 zstr.total_out = 0;
2355
2356 if (deflate(&zstr,Z_FINISH)!=Z_STREAM_END) {
2357 compsize = 0;
2358 return sizeof(png_1x1);
2359 }
2360
2361 compsize = zstr.total_out;
2362 #else /* HAVE_ZLIB_H */
2363 compsize = compbuffsize;
2364 if (charts_fake_compress(rawchart,rawchartsize,compbuff,&compsize)<0) {
2365 compsize = 0;
2366 return sizeof(png_1x1);
2367 }
2368 #endif /* HAVE_ZLIB_H */
2369
2370 return sizeof(png_header)+compsize+sizeof(png_tailer);
2371 }
2372
charts_get_png(uint8_t * buff)2373 void charts_get_png(uint8_t *buff) {
2374 uint8_t *ptr;
2375 if (compsize==0) {
2376 memcpy(buff,png_1x1,sizeof(png_1x1));
2377 } else {
2378 memcpy(buff,png_header,sizeof(png_header));
2379 ptr = buff+(sizeof(png_header)-8);
2380 put32bit(&ptr,compsize);
2381 memcpy(buff+sizeof(png_header),compbuff,compsize);
2382 memcpy(buff+sizeof(png_header)+compsize,png_tailer,sizeof(png_tailer));
2383 charts_fill_crc(buff,sizeof(png_header)+compsize+sizeof(png_tailer));
2384 }
2385 compsize=0;
2386 #ifdef USE_PTHREADS
2387 zassert(pthread_mutex_unlock(&glock));
2388 #endif
2389 }
2390