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