📄 trend.cc
字号:
/* * trend: display live data on a trend graph * Copyright(c) 2003-2005 by wave++ "Yuri D'Elia" <wavexx@users.sf.net> * Distributed under GNU LGPL WITHOUT ANY WARRANTY. *//* * Headers */// defaults#include "defaults.hh"#include "color.hh"#include "timer.hh"#include "rr.hh"// system headers#include <stdexcept>#include <vector>using std::vector;#include <iostream>using std::cout;using std::cerr;using std::cin;#include <string>using std::string;#include <utility>using std::pair;#include <limits>using std::numeric_limits;// c system headers#include <cstdlib>using std::strtod;using std::strtoul;#include <cstring>using std::memcpy;using std::strlen;using std::strpbrk;using std::strchr;#include <math.h>#include <stdio.h>#include <ctype.h>#include <sys/stat.h>#include <unistd.h>#include <errno.h>#include <pthread.h>#ifdef __sgi#include <mutex.h>#endif// OpenGL/GLU#include "gl.hh"#ifndef NAN#define NAN numeric_limits<double>::quiet_NaN()#endif/* * Data structures */struct Value{ Value() {} Value(double value, size_t count) : value(value), count(count) {} double value; size_t count;};struct Intr{ // nearest value Value near; // intersection double value; double dist;};booloperator<(const Intr& l, const Intr& r){ return (l.dist < r.dist);}struct Grid{ double res; unsigned long mayor;};struct GrSpec{ Grid x; Grid y;};/* * Graph/Program state */namespace{ // Basic data const char* fileName; pthread_mutex_t mutex; volatile unsigned long damaged = 0; Trend::input_t input = Trend::input; Trend::format_t format = Trend::format; // Data and fixed parameters rr<Value>* rrData; Value* rrBuf; Value* rrEnd; double loLimit; double hiLimit; double grZero = 0.; // Visual/Fixed settings size_t history; size_t divisions; size_t offset; const char* title = NULL; GLfloat backCol[3]; GLfloat textCol[3]; GLfloat gridCol[3]; GLfloat lineCol[3]; GLfloat markCol[3]; GLfloat intrCol[3]; // Visual/Changeable settings int width; int height; int lc; bool autoLimit; bool dimmed = Trend::dimmed; bool fifo = Trend::fifo; bool smooth = Trend::smooth; bool scroll = Trend::scroll; bool values = Trend::values; bool marker = Trend::marker; bool grid = Trend::grid; GrSpec grSpec; bool paused = false; // Indicator status bool intr = false; bool intrFg; double intrX; double intrY; // Distribution graph bool distrib = Trend::distrib; vector<double> distribData; // Latency bool latency = Trend::latency; ATimer atLat(Trend::latAvg);}/* * I/O and data manipulation */// fill the round robin consistently with a single valuevoidfillRr(double value, size_t start = 0){ Value buf; buf.count = start; buf.value = value; for(size_t i = 0; i != history; ++i) { rrData->push_back(buf); if(!(++buf.count % divisions)) buf.count = 0; }}// skip whitespacevoidskipSpc(FILE* fd){ int c; do { c = getc_unlocked(fd); } while(isspace(c) && c != EOF); ungetc(c, fd);}// skip a space-separated stringvoidskipStr(FILE* fd){ int c; do { c = getc_unlocked(fd); } while(!isspace(c) && c != EOF); ungetc(c, fd);}// read a space-separated stringchar*readStr(FILE* fd, char* buf, size_t len){ char* p(buf); int c; while(len--) { c = getc_unlocked(fd); if(c == EOF || isspace(c)) { ungetc(c, fd); return p; } *p++ = c; } // overflow return NULL;}// read a number from an ascii streamdoublereadANum(FILE* fd){ // read a number double num; for(;;) { // read skipSpc(fd); char buf[Trend::maxNumLen]; char* end = readStr(fd, buf, sizeof(buf) - 1); if(feof(fd)) return NAN; if(!end) { // long string, skip it. skipStr(fd); continue; } *end = 0; // convert the number num = strtod(buf, &end); if(end != buf) break; } return num;}// read a number from a binary streamtemplate <class T> doublereadNum(FILE* fd){ T buf; return (fread(&buf, sizeof(buf), 1, fd)? static_cast<double>(buf): NAN);}// read up to the first number in the stream using the current formatdoublereadFNum(FILE* fd){ double num; switch(format) { case Trend::f_ascii: num = readANum(fd); break; case Trend::f_float: num = readNum<float>(fd); break; case Trend::f_double: num = readNum<double>(fd); break; case Trend::f_short: num = readNum<short>(fd); break; case Trend::f_int: num = readNum<int>(fd); break; case Trend::f_long: num = readNum<long>(fd); break; } return num;}// producer threadvoid*producer(void* prg){ // iostreams under gcc 3.x are completely unusable for advanced tasks such as // customizable buffering/locking/etc. They also removed the (really // standard) ->fd() access for "encapsulation"... FILE* in; for(size_t pos = (history % divisions);;) { // open the file and disable buffering in = fopen(fileName, "r"); if(!in) break; setvbuf(in, NULL, _IONBF, 0); // check for useless file types struct stat stBuf; fstat(fileno(in), &stBuf); if(S_ISDIR(stBuf.st_mode)) break; // first value for incremental data double old, num; if(input != Trend::absolute) old = readFNum(in); // read all data while(!isnan((num = readFNum(in)))) { // determine the actual value switch(input) { case Trend::incremental: { double tmp = num; num = (num - old); old = tmp; } break; case Trend::differential: old += num; num = old; break; } // append the value rrData->push_back(Value(num, pos));#ifdef __sgi add_then_test((unsigned long*)(&damaged), 1);#else pthread_mutex_lock(&mutex); ++damaged; pthread_mutex_unlock(&mutex);#endif // wrap pos when possible if(!(++pos % divisions)) pos = 0; } // close the stream and terminate the loop for regular files fclose(in); if(S_ISREG(stBuf.st_mode) || S_ISBLK(stBuf.st_mode)) break; } // should never get so far cerr << reinterpret_cast<char*>(prg) << ": producer thread exiting\n"; return NULL;}/* * OpenGL functions */// project model coordinatesvoidproject(double xi, double yi, int& xo, int& yo){ int width(distrib? ::width - Trend::distribWidth: ::width); int off(distrib? Trend::distribWidth: 0); xo = (off + static_cast<int>(xi * width / divisions)); yo = static_cast<int>((yi - loLimit) * height / (hiLimit - loLimit));}// unproject video to model coordinatesvoidunproject(int xi, int yi, double& xo, double& yo){ int width = (distrib? ::width - Trend::distribWidth: ::width); int xr = (distrib? xi - Trend::distribWidth: xi); xo = (static_cast<double>(divisions) * xr / width); yo = hiLimit - (static_cast<double>(hiLimit - loLimit) * yi / height);}// OpenGL state initializervoidinit(){ // Smoothing if(smooth) { glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); } else glDisable(GL_LINE_SMOOTH); // Blending should be enabled by default glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Clear color glClearColor(backCol[0], backCol[1], backCol[2], 0.0);}// Resize handlervoidreshape(const int w, const int h){ width = w; height = h; glViewport(0, 0, w, h);}// write a normal stringvoiddrawString(const int x, const int y, const char* str){ glRasterPos2i(x, y); for(const char* p = str; *p; ++p) glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *p);}// write strings in the lower-left cornervoiddrawLEString(const char* str){ drawString(Trend::strSpc, Trend::strSpc + Trend::fontHeight * lc++, str);}// write an on-screen string using video coordinatesvoiddrawOSString(const int x, const int y, const char* str){ using Trend::strSpc; using Trend::fontHeight; int len(strlen(str) * 8); int rx(x + strSpc); int ry(y + strSpc); // check x if((rx + strSpc + len) > width) rx = width - len - strSpc; if(rx < 0) rx = strSpc; // check y if((ry + strSpc + fontHeight) > height) ry = height - fontHeight - strSpc; if(ry < 0) ry = strSpc; drawString(rx, ry, str);}voiddrawGridX(double gridres){ // horizontal lines double it; glBegin(GL_LINES); if(scroll) for(it = divisions; it > 0; it -= gridres) { glVertex2d(it, loLimit); glVertex2d(it, hiLimit); } else for(it = gridres; it <= divisions; it += gridres) { glVertex2d(it, loLimit); glVertex2d(it, hiLimit); } glEnd();}voiddrawGridY(double gridres){ // vertical lines double it = loLimit - fmod(loLimit - grZero, gridres); if(it <= loLimit) it += gridres; glBegin(GL_LINES); for(; it < hiLimit; it += gridres) { glVertex2d(0, it); glVertex2d(divisions, it); } glEnd();}voiddrawGrid(){ using Trend::maxGridDens; double r, d; // x r = (divisions / grSpec.x.res); d = (width / maxGridDens); if(r < (d * grSpec.x.mayor)) { // minor lines if(grSpec.x.mayor != 1 && r < d) { glColor4f(gridCol[0], gridCol[1], gridCol[2], 0.5); drawGridX(grSpec.x.res); } // mayor lines if(grSpec.x.mayor) { glColor3fv(gridCol); drawGridX(grSpec.x.res * grSpec.x.mayor); } } // y r = ((hiLimit - loLimit) / grSpec.y.res); d = (height / maxGridDens); if(r < (d * grSpec.y.mayor)) { // minor lines if(grSpec.y.mayor != 1 && r < d) { glColor4f(gridCol[0], gridCol[1], gridCol[2], 0.5); drawGridY(grSpec.y.res); } // mayor lines if(grSpec.y.mayor) { glColor3fv(gridCol); drawGridY(grSpec.y.res * grSpec.y.mayor); } }}voiddrawMarker(const double x){ glColor3fv(markCol); glBegin(GL_LINES); glVertex2d(x, loLimit); glVertex2d(x, hiLimit); glEnd();}voiddrawCircle(const int x, const int y){ using Trend::intrRad; // ok, it's not really a circle. glBegin(GL_LINE_LOOP); glVertex2i(x - intrRad, y); glVertex2i(x, y + intrRad); glVertex2i(x + intrRad, y); glVertex2i(x, y - intrRad); glEnd();}// get drawing position based on current settingssize_tgetPosition(size_t pos, const Value& value){ return ((scroll? pos: value.count) % divisions);}size_tdrawLine(){ const Value* it = rrBuf; const Value* end = rrEnd; const size_t mark(history + offset - divisions); size_t pos; glBegin(GL_LINE_STRIP); for(size_t i = offset; it != end; ++i, ++it) { // shade the color double alpha(dimmed? (i > mark? 1.: .5): (static_cast<float>(i - offset) / history)); glColor4f(lineCol[0], lineCol[1], lineCol[2], alpha); pos = getPosition(i, *it); if(!pos) { // Cursor at the end glVertex2d(divisions, it->value); glEnd(); glBegin(GL_LINE_STRIP); glVertex2d(0, it->value); } else glVertex2d(pos, it->value); } glEnd(); return pos;}voiddrawDistrib(){ // reallocate only if necessary. we must avoid to reallocate in order to // not fragment memory (resize() on gcc 3 isn't very friendly) if(distribData.size() != static_cast<size_t>(height)) distribData.resize(height); // calculate distribution const Value* it = rrBuf; const Value* end = rrEnd - 1; distribData.assign(distribData.size(), 0.); double max = 0; for(; it != end; ++it) { const Value* a = it; const Value* b = (it + 1); // projection double mul = (static_cast<double>(height) / (hiLimit - loLimit)); int begin = static_cast<int>(mul * (a->value - loLimit)); int end = static_cast<int>(mul * (b->value - loLimit)); if(begin > end) std::swap(begin, end); // fixation if(end < 0 || begin > height) continue; if(begin < 0) begin = 0; if(end > height) end = height; // integration for(int y = begin; y != end; ++y) { if(++distribData[y] > max) max = distribData[y]; } } if(max != 0.) max = 1. / max; // draw the results (optimize for continue zones) glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, width, 0, height); using Trend::distribWidth; double oldColor = distribData[0] * max; glColor3d(oldColor, oldColor, oldColor); glBegin(GL_QUADS); glVertex2i(0, 0); glVertex2i(distribWidth, 0); for(int y = 1; y != (height - 1); ++y) { double color = distribData[y] * max; if(color != oldColor) { glVertex2i(distribWidth, y); glVertex2i(0, y); oldColor = color; glColor3d(color, color, color); glVertex2i(0, y); glVertex2i(distribWidth, y); } } glVertex2i(distribWidth, height); glVertex2i(0, height); glEnd(); glPopMatrix();}voiddrawIntr(){ // initial color and current position glColor3fv(intrCol); glBegin(GL_LINES); glVertex2d(intrX, loLimit); glVertex2d(intrX, hiLimit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -