#include #include #include #include #include #include #include #include "logger.h" #include "sics.h" #define LOGGER_NAN -999999. #define ONE_YEAR (366*24*3600) #define LLEN 1024 /* max. number of dirs in path */ #define MAX_DIRS 16 typedef enum { NUMERIC, TEXT } CompType; typedef struct { time_t t; float y; } Point; typedef struct { SConnection *pCon; int exact; CompType type; time_t step; time_t tlim; /* 0: initial state */ Point best; /* best point, to be written if tlim > 0 */ Point written; /* last written point */ Point last; /* last point */ char buf[LLEN]; int np; char *none; char *header; time_t period; int omitEqual; } Compressor; static char *dirs[MAX_DIRS] = {NULL}; static int ndirs=0; static void InitCompressor(Compressor *c, SConnection *pCon, time_t step) { c->pCon = pCon; c->step = step; c->tlim = 0; c->last.y = LOGGER_NAN; c->last.t = 0; c->buf[0]='\0'; c->written.t = 0; c->written.y = LOGGER_NAN; c->omitEqual = 1; } static void OutFloat(Compressor *c, Point p) { char *value; if (p.y == LOGGER_NAN) { if (c->omitEqual && c->written.y == LOGGER_NAN) return; if (c->none) { value = c->none; } else { value = ""; } } else { if (c->omitEqual && c->written.y == p.y) return; snprintf(c->buf, sizeof(c->buf), "%g", p.y); value = c->buf; } SCPrintf(c->pCon, eWarning, "%ld %s", (long)(p.t - c->written.t), value); c->written = p; c->np--; } static void WriteHeader(Compressor *c) { if (c->header) { SCPrintf(c->pCon, eWarning, "*%s period %ld\n", c->header, c->period); c->header = NULL; } } static void PutValue(Compressor *c, time_t t, char *value) { char *p; Point new; WriteHeader(c); if (c->type == NUMERIC) { new.y = strtod(value, &p); if (p == value) { new.y = LOGGER_NAN; } else { if (new.y == LOGGER_NAN) new.y *= 1.0000002; } new.t = t; if (t >= c->tlim) { /* a new interval starts */ if (c->tlim == 0) { c->tlim = t; } else if (c->best.y != c->written.y) { OutFloat(c, c->best); } c->best = new; c->tlim += c->step; if (t > c->tlim) { if (c->last.y != c->written.y) { OutFloat(c, c->last); } c->tlim = t + c->step; } } else { /* not the first point */ if (fabs(new.y - c->best.y) > fabs(c->written.y - c->best.y)) { c->best = new; } } c->last = new; } else if (c->type == TEXT) { if (0 != strncmp(value, c->buf, sizeof(c->buf)-1)) { snprintf(c->buf, sizeof(c->buf), "%s", value); SCPrintf(c->pCon, eWarning, "%ld %s\n", (long)(t - c->written.t), value); c->written.t = t; c->np--; } } } static void PutFinish(Compressor *c, time_t now) { char value[32]; if (c->tlim != 0) { /* there is data already */ c->omitEqual = 0; if (c->type == NUMERIC) { if (now > c->last.t + c->period) { /* copy last value to the actual time */ if (c->last.y != LOGGER_NAN) { snprintf(value, sizeof value, "%g", c->last.y); PutValue(c, now, value); } if (c->best.t > c->written.t) { OutFloat(c, c->best); /* write last buffered value */ } } } if (now > c->last.t) { PutValue(c, now, c->buf); } } } /*--------------------------------------------------------------------------*/ static long getOpt(char *line, int *isdst, int *exact) { long lxs; char *opt; opt = strstr(line, "isdst"); if (opt) { sscanf(opt, "isdst %d", isdst); } opt = strstr(line, "exact"); if (opt) { sscanf(opt, "exact %d", exact); } opt = strstr(line, "period"); if (opt) { sscanf(opt, "period %ld", &lxs); return lxs; } else { return -1; } } /*--------------------------------------------------------------------------*/ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { /* Usage: graph [ none ] np [ ...] graph text graph [ ...] and are seconds since epoch (unix time) or, if the value is below one day, a time relative to the actual time The is optional. if not given, unknown values are returned as empty strings is the maximal number of points to be returned. If more values are present and the values are numeric, the data is reduced. If the data is not numeric, the last values may be skipped in order to avoid overflow. is the name of a variable (several vaiables may be given). Note that slashes are converted to dots, and that the first slash is ignored. The seconds variant is for text values, which can not be reduced. In any case, all values are returned. The third variant is old style and can be replaced by the first variant, where = ( - ) / + 2 Output format: First line: returning the actual time on the server (this time is used for relative times) For each variable which has data in the given interval, the variable name is returned preceeded by a '*', followed by some infos* separated with blanks. After the header line for every data point a line follows with a time relative to the last point and the value. The first time value relative to zero, e.g. absolute. The data value is valid until the next datapoint. Empty values are returned as an empty string or as the . At the very end * is returned. is 1, when the data had to be reduced, 0 else is 1, when overflow occured, 0 else. Overflow may happen only when np is given and a text variable was demanded. *actually only one info exists: period . This is the update rate in seconds. As equal values are not transmitted, two points (t1,y1) and (t2,y2) with a distance (t2 - t1) > period should not be connected directly. The plot software should generate an intermediate point at (t2-period,y1). */ time_t from, to, step, xs, lastt; char *p, *varp; int i, j, iarg, pathLen, iret, loss, np; int inRange; int yday=0; time_t t, startim; struct tm tm; char stim[32], path[LLEN], line[LLEN], lastval[LLEN]; char *lin, *val, *stp; FILE *fil; Compressor c={0}; float yy, lasty; CompType type0; DIR *dr; char *opt; int isdst; int overflow; time_t now, nows[MAX_DIRS], nowi; char var[256]; char dirPathBuffer[1024]; char *dirPath; int idir; char *colon; /* argtolower(argc, argv); */ if (argc < 4) goto illarg; now = time(NULL); from = strtol(argv[1], &p, 0); /* unix time, not year 2038 safe */ if (p == argv[1]) goto illarg; to = strtol(argv[2], &p, 0); if (p == argv[2]) goto illarg; if (from < ONE_YEAR) { from += now; } if (to < ONE_YEAR) { to += now; } iarg = 3; while (1) { if (iarg>=argc) goto illarg; if (strcasecmp(argv[iarg],"text") == 0) { /* non-numeric values */ iarg++; step = 1; type0 = TEXT; np = to - from + 2; break; } else if (strcasecmp(argv[iarg],"none") == 0) { /* none */ iarg++; if (iarg >= argc) goto illarg; c.none = argv[iarg]; iarg++; } else if (strcasecmp(argv[iarg],"np") == 0) { /* max. number of points */ iarg++; if (iarg >= argc) goto illarg; type0 = NUMERIC; np = strtol(argv[iarg], &p, 0); if (p == argv[iarg]) goto illarg; iarg++; if (to <= from) { step = 1; } else if (np <= 2) { step = to - from; } else { step = (to - from) / (np - 2) + 1; } break; } else { step = strtol(argv[iarg], &p, 0); if (p == argv[iarg]) goto illarg; iarg++; if (step <= 0) step = 1; type0 = NUMERIC; np = (from - to) / step + 2; } } if (step <= 0) step = 1; snprintf(line, sizeof line, "%ld\n", (long)now); SCWrite(pCon, line, eWarning); dirPath = IFindOption(pSICSOptions, "LogReaderPath"); if (dirPath == NULL) { /* for compatibility, check */ dirs[0] = IFindOption(pSICSOptions, "LoggerDir"); if (dirs[0] == NULL) { SCWrite(pCon, "ERROR: LoggerPath not found", eError); return 0; } nows[0] = LoggerGetLastLife(NULL); if (dirs[1] == NULL) { dirs[1] = IFindOption(pSICSOptions, "LoggerDir2"); nows[1] = LoggerGetLastLife(dirs[1]); } } else { snprintf(dirPathBuffer, sizeof dirPathBuffer, "%s", dirPath); dirPath = dirPathBuffer; for (ndirs = 0; ndirs < MAX_DIRS; ndirs++) { dirs[ndirs] = dirPath; colon = strchr(dirPath, ':'); if (colon != NULL) { *colon = '\0'; } if (ndirs == 0) { nows[0] = LoggerGetLastLife(NULL); } else { nows[ndirs] = LoggerGetLastLife(dirPath); } if (colon == NULL) { ndirs++; break; } dirPath = colon + 1; } } loss = 0; overflow = 0; for (i=iarg; i= 0) { if (c.period == 0) { c.type = TEXT; } else { c.type = type0; xs = c.period; } if (xs < step) { loss = 1; xs = step; } } } } } if (fil == NULL) { lin = NULL; } else { do { lin = fgets(line, sizeof line, fil); /* printf("%s\n", line); */ if (lin == NULL) break; if (line[0] == '#') { c.period = getOpt(line, &isdst, &c.exact); if (c.period >= 0) { if (c.period == 0) { c.type = TEXT; } else { c.type = type0; xs = c.period; } if (xs < step) { loss = 1; xs = step; } } lin[0]='\0'; } else { p = strchr(line, '\n'); if (p) *p='\0'; p = strchr(line, '#'); if (p) *p='\0'; } } while (lin[0] == '\0'); } if (lin != NULL) { /* printf(" %s\n", line); */ p = strchr(line, '\t'); if (p) { *p='\0'; val = p+1; } else { val = ""; } p = strchr(val, '\t'); if (p) { stp = p+1; *p='\0'; iret = sscanf(stp, "%ld", &c.period); if (iret == 1) { if (c.period == 0) { c.type = TEXT; } else { c.type = type0; xs = c.period; } if (xs < step) { loss = 1; xs = step; } } } iret = sscanf(line, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (iret != 3) { lin = NULL; } else { tm.tm_isdst = isdst; t=mktime(&tm); if (!inRange) { if (t < startim) { lastval[0]='\0'; strncat(lastval, val, sizeof lastval - 1); lastt = t; } else { inRange=1; if (lastt != 0) { PutValue(&c, lastt, lastval); } PutValue(&c, t, val); } } else { PutValue(&c, t, val); } } } if (lin == NULL) { tm.tm_hour = 24; /* try next day */ tm.tm_min = 0; tm.tm_sec = 0; tm.tm_isdst = -1; startim=mktime(&tm); continue; } } if (!inRange) { if (lastt != 0) { PutValue(&c, lastt, lastval); } } WriteHeader(&c); /* write header, if not already done */ PutFinish(&c, nowi); if (fil) { fclose(fil); fil = NULL; } if (c.np < 0) overflow = 1; } snprintf(line, sizeof line, "*%d %d\n", loss, overflow); SCWrite(pCon, line, eWarning); return 1; illarg: SCWrite(pCon, "illegal argument(s)", eError); return 0; } /*--------------------------------------------------------------------------*/ static void KillLogReader(void *data) { Logger *p, *next; KillDummy(data); LoggerFreeAll(); } /*--------------------------------------------------------------------------*/ void LogReaderInit(void) { AddCommand(pServ->pSics,"Graph",LogReader,KillLogReader,NULL); }