#include "seaset.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "instr_hosts.h" #include "utils.h" #define MAX_LABELS 20 #define MAX_FUTURE 600 #define ONE_YEAR 365*24*3600 static int prt = 2; void setprintit(int p) { prt = p; } int printit(void) { return prt>0; } SeaRow::SeaRow(QWidget *parent) : QScrollView(parent) { this->plot = 0; state = newRow; setHScrollBarMode(QScrollView::AlwaysOff); setVScrollBarMode(QScrollView::AlwaysOff); usedLegend = 0; timeLabel = 0; }; void SeaRow::resizeEvent(QResizeEvent *e) { int w, rightW, legW, rowW, rowH; SeaRow *row; bool isfirstrow; if (e->size() == e->oldSize()) { return; } if (isHidden()) { if (set->showPlot) { set->saveRowHeight(this, 0); } return; } rowH = e->size().height(); // row height if (rowH == 1) { rowH = 0; } if (set->showPlot) { set->saveRowHeight(this, rowH); } if (right->isHidden() && rowH > 0) { state = shownRow; if (set->showPlot) { set->reread = true; right->show(); hBox->show(); } } rightW = bBox->sizeHint().width(); for (row = set->rows.first(); row != 0; set->rows.findRef(row), row = set->rows.next()) { if (row->state != hiddenRow) { legW = row->leg->sizeHint().width(); legW += row->legScroll->width() - row->legScroll->visibleWidth(); if (legW > rightW) { rightW = legW; } } } rowW = e->size().width(); isfirstrow = true; for (row = set->rows.first(); row != 0; set->rows.findRef(row), row = set->rows.next()) { if (row->state != hiddenRow) { if (row->right->width() != rightW) { row->right->setFixedWidth(rightW); } w = e->size().width() - rightW - hBox->layout()->spacing(); if (row->plot->width() != w && w > 1) { row->plot->setFixedWidth(w); } if (row == this) { if (plot->height() != rowH) { setInnerHeights(isfirstrow, rowH); } if (rowW != hBox->width()) { hBox->setFixedWidth(rowW); } } isfirstrow = false; } } set->setFirstRow(); } void SeaRow::setInnerHeights(bool isfirstrow, int rowH) { int legH, butH, axisH; axisH = 0; butH = bBox->height(); legH = QMAX(rowH - butH, butH); if (isfirstrow) { // space for time label legH -= timeLabel->height(); axisH = 0; } else { // plot height has to be increased by this value to hide the time axis axisH = plot->axis(SeaPlot::xBottom)->height(); } plot->setFixedHeight(rowH + axisH); hBox->setFixedHeight(rowH + axisH); if (legH > butH) { legScroll->setFixedHeight(legH); } } void SeaRow::hideRow() { state = hiddenRow; set->saveRowHeight(this, 0); right->hide(); hBox->hide(); set->hideRow(); } QSize SeaRow::minimumSizeHint() const { QSize size = QScrollView::minimumSizeHint(); size.setHeight(30); return size; } SeaData::SeaData(const QString &name, const QString &label, const QString &plotName, int color) { this->name = name; this->label = label; this->plotName = plotName; step_m = 1; lastx = 0; size_m = 0; clr = false; dirty = true; key = 0; plot = 0; row = 0; bold = true; decipos = 3; size = 0; style = color; //printf("curve %s %s\n", name.latin1(), plotName.latin1()); } SeaData::~SeaData() { } bool SeaData::isActive() { return bold && row != 0 && row->state != hiddenRow; } void SeaData::init(time_t step, int siz) { step_m = step; size_m = 0; lastx = 1e30; modified = true; if (siz > size) { if (size > 0) { free(x); free(y); } size = siz; x = (double *)calloc(size, sizeof(*x)); y = (double *)calloc(size, sizeof(*y)); } } int SeaData::addValue(double xx, QString value) { double yy; bool ok; if (size_m >= size) return -1; while (size_m > 0 && xx <= x[size_m-1] + 0.5) { size_m--; } if (period >= 0) { if (size_m > 0 && xx > lastx + 2 * period) { /* gap, additional point needed */ x[size_m] = xx - period; y[size_m] = y[size_m-1]; lastx = 1e30; size_m++; if (size_m >= size) return -1; } } yy = value.toDouble(&ok); //iret = sscanf(value, "%lf", &yy); if (ok) { // valid value /* if (size_m >= 2 && yy == y[size_m-1] && yy == y[size_m-2]) { size_m--; // do not draw the middle of 3 points on a horizontal line } */ x[size_m] = xx; y[size_m] = yy; lastx = xx; size_m++; modified = true; } else if (size_m > 0) { /* invalid value -> an undefined value is marked with DATA_UNDEF */ if (size_m > 0 && y[size_m-1] == DATA_UNDEF) { return size_m; } x[size_m] = x[size_m-1]; y[size_m] = DATA_UNDEF; lastx = 1e30; size_m++; modified = true; } return size_m; } bool SeaData::update() { if (plot) { if (modified) { modified = false; plot->setCurveRawData(key, x, y, size_m); return true; } } return false; } void SeaData::showValueAt(double at) { QString text(""); int pos, epos; int i, l; double value = DATA_UNDEF; if (size_m > 0) { if (at == DATA_UNDEF) { at = x[size_m-1]; } } for (i = size_m-1; i >= 0; i--) { if (x[i] <= at + 0.5) { value = y[i]; // if (i > 0 && x[i-1] >= x[i]-0.5 && value == DATA_UNDEF) { // value = y[i-1]; // } break; } } if (value != DATA_UNDEF) { text.sprintf(" %9.5g", value); pos = text.find('.'); epos = text.find('e'); if (epos >= 0) { epos ++; if (text[epos] == '+') { text.remove(epos, 1); } else if (text[epos] == '-') { epos ++; } if (text[epos] == '0') { text.remove(epos, 1); } pos = text.length(); if (pos > 10) { // remove last digit before 'e' when number too long epos = text.find('e'); text.remove(epos - 1, 1); } } else { l = text.length(); if (pos < 0) pos = l; while (pos < decipos && pos < 2 && l < 10) { text.prepend(' '); pos++; l++; } } while (pos > decipos && text[1] == ' ') { text.remove(0, 1); text.append(' '); pos--; } text.truncate(10); } shownValue->setText(text); } void SeaSet::clrDataList() { SeaData *d; for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.first()) { dataList.remove(); delete d; } } SeaData *SeaSet::findData(const QString &name) { SeaData *d; for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.next()) { if (d->name.compare(name) == 0) { return d; } } return NULL; } void SeaSet::insertData(const QString &name, const QString &label, const QString &plotName, const int color) { SeaData *d; QString pName; if (plotName == 0) { pName = ""; } else { pName = plotName; } if (name.compare("!") < 0) return; // do not make curves with blank name d = findData(name); if (d) { d->clr = false; d->label = label; d->plotName = pName; //d->bold = true; d->style = color; return; } dataList.append(new SeaData(name, label, pName, color)); } void SeaSet::finishDataList() { SeaData *d; SeaPlot *plot; bool dirty; SeaRow *row, *unused; LegendButton *legButton; QLabel *valueLabel; QFont monospace("Courier"); monospace.setStyleHint(QFont::TypeWriter); QFontMetrics fm(monospace); QGridLayout *gridLayout; uint idx; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { row->plot->removeAll(); row->usedLegend = 0; } for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.next()) { if (! d->clr) { // find plot with that name unused = 0; plot=0; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { plot = row->plot; if (plot->tag.compare(d->plotName) == 0) break; if (!unused && plot->tag.compare("") == 0) { unused = row; } } if (!row) { if (unused) { row = unused; plot = row->plot; plot->tag = d->plotName; row->setTag(d->plotName); } else { row = newPlot(d->plotName); plot = row->plot; } //plot->setAxisTitle(SeaPlot::yLeft, d->plotName); } if (row->legendList.count() > row->usedLegend) { legButton = row->legendList.at(row->usedLegend); valueLabel = legButton->valueLabel; legButton = plot->putCurve(d, row->leg, legButton); d->row = row; gridLayout = dynamic_cast(row->leg->layout()); legButton->label->setText(d->label); legButton->show(); legButton->label->show(); valueLabel->show(); // printf("recycle %s %s\n", plot->tag.latin1(), d->label.latin1()); } else { valueLabel = new QLabel(row->leg); valueLabel->setFont(monospace); valueLabel->setFixedWidth(fm.width(" -0.0000e-0") + 2 * valueLabel->frameWidth()); legButton = plot->putCurve(d, row->leg); d->row = row; legButton->valueLabel = valueLabel; row->legendList.append(legButton); gridLayout = dynamic_cast(row->leg->layout()); assert(gridLayout); gridLayout->addWidget(legButton, d->key, 0, Qt::AlignLeft + Qt::AlignBottom); gridLayout->addWidget(legButton->label, d->key, 1, Qt::AlignLeft + Qt::AlignBottom); gridLayout->addWidget(valueLabel, d->key, 2, Qt::AlignRight + Qt::AlignBottom); legButton->show(); legButton->label->show(); valueLabel->show(); // printf("new %s %s\n", plot->tag.latin1(), d->label.latin1()); } row->usedLegend++; d->shownValue = valueLabel; gridLayout->activate(); if (showPlot) { row->hBox->show(); row->right->show(); } else { row->hBox->hide(); row->right->hide(); } } } dirty = true; while (dirty) { dirty= false; for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.next()) { if (d->clr) { dataList.remove(d); delete d; dirty = true; } } } for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { for (idx = row->usedLegend; idx < row->legendList.count(); idx++) { legButton = row->legendList.at(idx); legButton->setCurvePen(QPen(Qt::NoPen)); legButton->valueLabel->setText(""); legButton->label->setText(""); } if (row->usedLegend == 0) { row->hide(); row->state = hiddenRow; } else { row->show(); if (row->state == hiddenRow) row->state = shownRow; row->plot->autoColors(); } } adjustSizes(); readNewReplot(); } void SeaSet::saveRowHeight(SeaRow *row, int height) { int *rowheight; // printf("*** save %s %d\n", row->plot->tag.latin1(), height); rowheight = rowHeightDict.find(row->plot->tag); if (rowheight) { *rowheight = height; } else { rowHeightDict.insert(row->plot->tag, new int(height)); } } void SeaSet::saveBoldState(SeaData *data) { bool *boldstate; boldstate = boldDict.find(data->name); if (boldstate) { *boldstate = data->bold; } else if (!data->bold) { boldDict.insert(data->name, new bool(false)); } } int SeaSet::getRowHeight(SeaRow *row) { int *rowheight; rowheight = rowHeightDict.find(row->plot->tag); if (rowheight) { // printf("** getRow %s %d\n", row->plot->tag.latin1(), *rowheight); return *rowheight; } else { // printf("** getRow %s %d\n", row->plot->tag.latin1(), -1); return -1; } } void SeaSet::getBoldState(SeaData *data) { bool *boldstate; boldstate = boldDict.find(data->name); if (boldstate) { data->bold = *boldstate; } else { data->bold = true; } } void SeaSet::adjustSizes() { QValueList sizes; int i, cnt, rowN, h, tot; int rowheight; SeaRow *row; bool isfirstrow; /* if (initSizes) { initSizes = false; sizes = split->sizes(); h = 400; for (i = 0; i<(int)sizes.count(); i++) { sizes[i] = h; h = 200; } split->setSizes(sizes); goto quit; } */ tot = 0; /* sum of known sizes */ cnt = 0; /* count of known sizes (first row is counted twice) */ sizes = split->sizes(); rowN = 0; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { rowheight = getRowHeight(row); if (rowheight == 0 && row->state != newRow) { row->state = hiddenRow; sizes[rowN] = 0; } else if (rowheight <= 0) { row->state = shownRow; sizes[rowN] = -1; } else { row->state = shownRow; cnt++; tot += rowheight; if (rowN == 0) { cnt++; } sizes[rowN] = rowheight; } rowN++; } assert(rowN == (int)sizes.count()); if (cnt == 0) { h = 200; } else { h = tot / cnt; } for (i = 0; i < rowN; i++) { if (sizes[i] < 0) { if (i == 0) { sizes[i] = h*2; } else { sizes[i] = h; } } } split->setSizes(sizes); //quit: isfirstrow = true; for (row = rows.first(), i = 0; row != 0; i++, rows.findRef(row), row = rows.next()) { if (row->state != hiddenRow) { rowheight = row->height(); if (rowheight > row->bBox->height()) { row->setInnerHeights(isfirstrow, rowheight); saveRowHeight(row, rowheight); } isfirstrow = false; } else { saveRowHeight(row, 0); } } setFirstRow(); return; } void SeaSet::setFirstRow() { SeaRow *row; for (row = rows.first(); row != 0; row = rows.next()) { if (row->state == shownRow) { // this is the first row if (row != firstRow) { if (firstRow) { firstRow->setInnerHeights(false, firstRow->height()); firstRow->timeLabel->setText(""); } firstRow = row; firstRow->setInnerHeights(true, firstRow->height()); } return; } } } SeaSet::SeaSet(QSplitter *parent, long range, const char *name) : QObject(parent, name), rowHeightDict(31, false), boldDict(127, false) { hostport = ""; sc = new SicsConnection(this); uc = new SicsConnection(this); uc->setUser("seauser seaser\n"); gc = new SicsConnection(this); // gc->debug = "G"; ec = new SicsConnection(this); connect(sc, SIGNAL(reconnected()), uc, SLOT(reconnect())); firstRow = 0; split = parent; labelWidth = 30; actLegendWidth = 10; legendWidth = 10; if (range > ONE_YEAR) { // range is definitly not a relative date startRange = range - 1800; endRange = range + 1800; } else { startRange = - range; endRange = QMIN(MAX_FUTURE, range / 2); } live = liveOff; lastRead = time(NULL); reread = true; undoPlot = 0; undoStart = 0; labelMode = 0; initSizes = true; refresh = 0; xSize = 1000; autoDo = false; getDo = false; getNewData = false; autoArg.vars = ""; base = 0; async_mode = async_idle; asyncTimer = new QTimer(this); connect(asyncTimer, SIGNAL(timeout()), SLOT(asyncHandler())); asyncTimer->start(1); meas.start(); showPlot = false; markerPos = DATA_UNDEF; markerWasOn = false; } void SeaSet::setHost(const QString &hostport) { static char cmd[128], psw[32]; static char *user; char host[128], instr[32]; char *hp = (char *)hostport.latin1(); char *p; int port; this->hostport = hostport; sc->setHost(hostport.latin1(), "sea"); uc->setHost(hostport.latin1(), "sea"); gc->setHost(hostport.latin1(), "graph"); ec->setHost(hostport.latin1(), "graph"); p = strchr(hp, ':'); if (p == NULL) { InstrHost("sea", hp, instr, sizeof instr, host, sizeof host, &port); if (strcmp(host, instr) == 0) { snprintf(psw, sizeof psw, "SSHPASS=%sLNS", strtoupper(instr)); user = instr; } else if (strcmp(host, "linse-c") == 0) { snprintf(psw, sizeof psw, "SSHPASS=1%dlns1", 7); user = "l_samenv"; } else { return; } putenv(psw); snprintf(cmd, sizeof cmd, "sshpass -e ssh -Y %s@%s sea start %s", user, host, instr); sc->startServer = cmd; for (int i=0; i<30; i++) { sc->handleBuffer(1); if (sc->connect_state == SicsConnection::connect_waitlogin) { break; } usleep(250000); } } } void SeaSet::setLive(bool on) { time_t range, now; if (lastRead >= 0) { now = lastRead; } else { now = time(NULL); } if (on) { if (live == liveOff) { range = QMAX(MAX_FUTURE, endRange - startRange); if (endRange < now - range && endRange > MAX_FUTURE*2) { live = liveAuto; } else { live = liveOn; if (startRange <= 0) { rescale(startRange, range / 2); } else { rescale(startRange, now + range / 2); } } } } else if (live != liveOff) { live = liveOff; if (endRange > now) { endRange = now; } rescale(startRange, endRange); } } void SeaSet::restart(const char *vars) { if (autoArg.vars == "" || autoArg.vars.compare(vars) != 0) { autoArgDo.vars = "0 "; autoArgDo.vars.append(vars); autoDo = true; } } void SeaSet::setTimeRange(time_t startrange, long seconds) { undoStart = 0; undoPlot = 0; undoEnabled(false); showPlot = false; rescale(startrange, startrange + QMIN(SEA_MAX_RANGE, QMAX(60, seconds))); } QString SeaSet::dateLabel(long dayOffset) { QString str; static char *wday[] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" }; static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; time_t t = base+12*3600+dayOffset*(24*3600); struct tm basetm = *localtime(&t); str.sprintf("%s.%d.%s.%2.2d", wday[basetm.tm_wday], basetm.tm_mday, month[basetm.tm_mon], basetm.tm_year % 100); return str; } int SeaSet::sicsCommand(QString &cmd, int graph) { int iret, cnt; const char *hp; SicsConnection *conn; if (graph) { conn = gc; } else { conn = sc; } iret = conn->command(cmd.latin1()); cnt = 2; while (iret <= 0) { if (iret == 0) { if (printit()) printf("empty response\n"); } else { hp = hostport.latin1(); if (hp == NULL || *hp == '\0') hp = "localhost"; if (printit()) printf("connection to %s failed\n", hp); } cnt --; if (cnt <= 0) return -1; if (printit()) printf("retry\n"); iret = conn->command(cmd.latin1()); } return iret; } int SeaSet::sendSicsCommand(QString &cmd, int graph) { int iret, cnt; const char *hp; SicsConnection *conn; if (graph) { conn = gc; } else { conn = sc; } iret = conn->sendCommand(cmd.latin1()); cnt = 2; while (iret <= 0) { if (iret == 0) { if (printit()) printf("empty response\n"); } else { hp = hostport.latin1(); if (hp == NULL || *hp == '\0') hp = "localhost"; if (printit()) printf("connection to %s failed\n", hp); } cnt --; if (cnt <= 0) return -1; if (printit()) printf("retry\n"); iret = conn->sendCommand(cmd.latin1()); } return iret; } static const char *yZoomPix[] = { "16 16 2 1", " c None", "X c #000000", " ", " XX ", " XXXX ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " ", " ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " XXXX ", " XX ", " " }; static const char *yDownPix[] = { "16 16 2 1", " c None", "X c #000000", " ", " ", " ", " ", " ", " ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " XXXX ", " XX ", " ", " ", " ", " ", }; static const char *yUpPix[] = { "16 16 2 1", " c None", "X c #000000", " ", " ", " ", " ", " XX ", " XXXX ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " ", " ", " ", " ", " ", " ", }; static const char *yMaxPix[] = { "16 16 2 1", " c None", "X c #000000", " ", "XXXXX XX XXXXX", " XXXX ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " ", " ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " XXXX ", "XXXXX XX XXXXX", " " }; static const char *closePix[] = { "16 16 2 1", " c None", "X c #000000", " ", " ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " XXXX ", " XX ", " XXXX ", " XX XX ", " XX XX ", " XX XX ", " XX XX ", " ", " ", " " }; static const char *logPix[] = { "16 16 2 1", " c None", "X c #000000", " ", " ", " XX ", " XX XXX XXX ", " XX XX XX XX XX ", " XX XX XX XXXX ", " XX XXX XX ", " XXX ", " X X ", " X XXXX ", " X X X X ", " X X X X ", " X X X X ", " ", " ", " " }; static const char *linPix[] = { "16 16 2 1", " c None", "X c #000000", " ", " ", " X ", " X XX XX ", " X X X X X ", " X X X XXX ", " X XX X ", " XX ", " XX XX ", " XX XXXXX ", " XX XX XX XX ", " XX XX XX XX ", " XX XX XX XX ", " ", " ", " " }; void SeaRow::linLog() { if (plot->toggleLinLog()) { lBut->setPixmap(pmLog); } else { lBut->setPixmap(pmLin); } } void SeaRow::setTag(QString tag) { int pos; pos = tag.findRev('_'); if (pos > 0) { tag.truncate(pos); } tag.prepend(" ["); tag.append("]"); tagLabel->setText(tag); } SeaRow *SeaSet::newPlot(const char *name) { QPushButton *zBut, *oBut, *uBut, *dBut, *cBut; SeaPlot *plot; QString txt; int rowN = rows.count(); QPixmap pmOut(yZoomPix); QPixmap pmMax(yMaxPix); QPixmap pmUp(yUpPix); QPixmap pmDown(yDownPix); QPixmap pmClose(closePix); SeaRow *row; QVBox *vbox; QBoxLayout *rowLayout, *bLayout, *vLayout; QScrollView *scroll; QSizePolicy narrow(QSizePolicy::Maximum, QSizePolicy::Maximum, 1, 0); QSizePolicy wide(QSizePolicy::Expanding, QSizePolicy::Expanding, 5, 0); //QSizePolicy high(QSizePolicy::Expanding, QSizePolicy::Expanding, 5, 10); int w, h; row = new SeaRow(split); row->setFrameStyle(0); row->hBox = new QHBox(row, "rowHbox"); rowLayout = dynamic_cast(row->hBox->layout()); assert(rowLayout); row->hBox->setSpacing(6); rowLayout->setAutoAdd(false); row->set = this; row->pmLog = (const char **)logPix; row->pmLin = linPix; split->setResizeMode(row, QSplitter::Stretch); txt.sprintf("plot%d", rowN); plot = new SeaPlot(row->hBox, this, txt); plot->tag = name; plot->setSizePolicy(wide); row->plot = plot; rowLayout->addWidget(plot); vbox = new QVBox(row->hBox, "vbox"); vLayout = dynamic_cast(vbox->layout()); vLayout->setAutoAdd(false); vbox->setSizePolicy(narrow); vLayout->setAlignment(Qt::AlignTop); rowLayout->addWidget(vbox, 0, Qt::AlignTop); row->right = vbox; row->bBox = new QHBox(vbox, "bBox"); bLayout = dynamic_cast(row->bBox->layout()); assert(bLayout); bLayout->setAutoAdd(false); vLayout->addWidget(row->bBox); scroll = new QScrollView(vbox, "scroll", Qt::WNoAutoErase); scroll->setHScrollBarMode(QScrollView::AlwaysOff); scroll->setFrameStyle(0); scroll->setSizePolicy(wide); row->legScroll = scroll; vLayout->addWidget(scroll); row->timeLabel = new QLabel(row->right); row->timeLabel->setText(""); vLayout->addWidget(row->timeLabel); row->timeLabel->setFont(row->plot->axisFont(QwtPlot::xBottom)); vLayout->addStretch(1); row->leg = new QGrid(3,scroll->viewport(),"legendGrid1"); SetEraseColor(scroll->viewport(), scroll->topLevelWidget()); row->leg->layout()->setAutoAdd(false); scroll->addChild(row->leg); uBut = new QPushButton(row->bBox); uBut->setPixmap(pmUp); bLayout->addWidget(uBut); dBut = new QPushButton(row->bBox); dBut->setPixmap(pmDown); bLayout->addWidget(dBut); oBut = new QPushButton(row->bBox); oBut->setPixmap(pmOut); bLayout->addWidget(oBut); zBut = new QPushButton(row->bBox); zBut->setPixmap(pmMax); bLayout->addWidget(zBut); row->lBut = new QPushButton(row->bBox); row->lBut->setPixmap(row->pmLin); bLayout->addWidget(row->lBut); row->tagLabel = new QLabel(row->bBox); row->setTag(name); bLayout->addWidget(row->tagLabel); bLayout->addStretch(1); cBut = new QPushButton(row->bBox); cBut->setPixmap(pmClose); bLayout->addWidget(cBut); w = QMAX(22, uBut->sizeHint().width() - 6); h = QMAX(22, uBut->sizeHint().height() - 6); if (w > h) { // Mac like h += 6; w -= 6; } uBut->setFixedSize(w, h); QToolTip::add(uBut, "scroll up"); oBut->setFixedSize(w, h); QToolTip::add(oBut, "zoom out vertically"); dBut->setFixedSize(w, h); QToolTip::add(dBut, "scroll down"); zBut->setFixedSize(w, h); QToolTip::add(zBut, "auto y-range"); cBut->setFixedSize(w, h); QToolTip::add(cBut, "hide this row"); row->lBut->setFixedSize(w, h); QToolTip::add(row->lBut, "log / lin scale"); zBut->setEnabled(false); plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); rows.append(row); connect(oBut, SIGNAL(clicked()), plot, SLOT(zoomOut())); connect(uBut, SIGNAL(clicked()), plot, SLOT(shiftUp())); connect(dBut, SIGNAL(clicked()), plot, SLOT(shiftDown())); connect(plot, SIGNAL(setAutoOff(bool)), zBut, SLOT(setEnabled(bool))); connect(zBut, SIGNAL(clicked()), plot, SLOT(zoomMax())); connect(cBut, SIGNAL(clicked()), row, SLOT(hideRow())); connect(row->lBut, SIGNAL(clicked()), row, SLOT(linLog())); row->leg->show(); return row; } void SeaSet::autoCurves(const char *vars) { if (vars == NULL) { autoArgDo.vars = ""; } else { autoArgDo.vars = vars; } autoDo = true; // printf("*** autoCurves\n"); } bool SeaSet::autoCurvesP1() { QString cmd; int iret; if (autoArg.vars != "" && autoArg.vars != "now") { return false; } if (autoArg.vars == "now") { cmd.sprintf("graph 0 0 text vars"); autoArg.vars = ""; } else { cmd.sprintf("graph %ld %ld text vars", startRange, endRange); //printf("DEBUG graph $ld %ld test vars\n", startRange, endRange); } iret = gc->sendCommand(cmd); if (iret < 0) { if (printit()) { printf("failed autoCurves\n"); exit(0); } } // printf("*** end autoCurvesP1\n"); return true; } void SeaSet::autoCurvesP2() { QString line; int iret; time_t now; QString item, pName, label; QStringList list, itemS; QStringList::iterator its; SeaData *d; long style; if (autoArg.vars == "") { iret = gc->getLine(line); if (iret <= 0) goto badresponse; /* get returned absolute time */ now = line.toLong(); //sscanf(line, "%ld", &now); iret = gc->getLine(line); if (iret <= 0) goto badresponse; if (!line.startsWith("*vars")) { if (printit()) printf("missing *vars\n"); goto badresponse; } iret = gc->getLine(line); if (iret <= 0) goto badresponse; iret = line.find(' '); if (iret >= 0) { autoArg.vars = line.mid(iret).stripWhiteSpace(); } base = 0; // force calcBase } else { time(&now); line = autoArg.vars; } if (base == 0) { if (startRange < 0) { startRange += now; } calcBase(startRange); if (endRange <= ONE_YEAR) { endRange += now; } } clrDataList(); while (!line.startsWith("*")) { iret = line.find(' '); if (iret >= 0) { list = QStringList::split(" ", line.mid(iret)); for (its = list.end(); its != list.begin(); ) { its--; item = *its; if (item.contains('|')) { itemS = QStringList::split("|", item, TRUE); } else { itemS = QStringList::split("/", item, TRUE); } style = -1; if (itemS.count() > 1) { pName = itemS[1]; item = itemS[0]; if (itemS.count() > 2) { if (itemS.count() > 3) { style = Convert2ColorIndex(itemS[3]); if (style < -1) { style = -1; // set bad colors to auto } } label = itemS[2]; } else { label = item; } } else { pName = ""; label = item; } if (pName == 0) { pName = ""; } d = findData(item); if (d) { dataList.remove(d); d->plotName = pName; d->clr = false; //d->bold = true; d->label = label; d->style = style; } else { d = new SeaData(item, label, pName, style); getBoldState(d); } dataList.prepend(d); } } if (autoArg.vars != "") break; iret = gc->getLine(line); if (iret <= 0) goto badresponse; }; //printf("DEBUG end graph\n"); if (prt) { printf("reading curves,\n"); } // printf("*** start finishDataList\n"); finishDataList(); // printf("*** end autoCurvesP2\n"); return; // 0 badresponse: if (printit()) { if (iret > 0) { printf("syntax error in response %s\n", line.latin1()); } else if (iret == 0) { printf("early end of response\n"); } else if (iret == -2) { printf("timeout on sics connection\n"); } else { printf("error on response\n"); } } return; // -1 } void SeaSet::getCurves(bool all, time_t from, time_t to) { getArgDo.all = all; getArgDo.append = false; getArgDo.from = from; getArgDo.to = to; getDo = true; } int SeaSet::getCurvesP1() { QString cmd; int iret, cnt; SeaData *d; // printf("*** do getCurvesP1\n"); if (getArg.from == getArg.to) { getArg.from = startRange; getArg.to = endRange; } cnt = 0; if (getArg.append) { curveStep = 1; } else { curveStep = long(getArg.to - getArg.from) / xSize + 1; } cmd.sprintf("graph %ld %ld np %d", getArg.from, getArg.to, xSize); for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.next()) { if ((d->dirty || getArg.all) && d->row->state != hiddenRow) { cmd.append(" "); cmd.append(d->name); cnt++; } } if (cnt == 0) { // printf("*** no data\n"); return 0; } //printf("*** %s\n", cmd.latin1()); iret = gc->sendCommand(cmd); return 1; } void SeaSet::getCurvesP2() { QString line; int iret, siz; time_t t, dt, tmax; SeaData *d; int iperiod; bool ok; // printf("*** do getCurvesP2\n"); assert(async_mode == async_get); iret = gc->getLine(line); if (iret <= 0) goto badresponse; //printf("> %s\n", line); /* get returned absolute time */ lastRead = line.toLong(); iret = gc->getLine(line); if (iret <= 0) goto badresponse; //printf("> %s\n", line.latin1()); if (!line.startsWith("*")) goto badresponse; tmax = 0; for (d = dataList.first(); d != 0; dataList.findRef(d), d = dataList.next()) { if ((d->dirty || getArg.all) && d->row->state != hiddenRow) { d->dirty = false; d->period = 1; iret = line.find(' '); if (iret >= 0) { iperiod = line.find("period", iret); if (iperiod >= iret) { d->period = line.mid(iperiod + 7).toLong(); } //if (period) { // sscanf(period, "period %lf", &d->period); //} line = line.left(iret); } if (d->name == line.mid(1)) { t = 0; if (!getArg.append) { d->init(curveStep, xSize*2); } siz = 0; while (1) { iret = gc->getLine(line); if (iret <= 0) goto badresponse; //printf("> %s\n", line); if (line.startsWith("*")) break; /* decode line to x[n] / y[n] */ dt = line.section(' ', 0, 0).toLong(&ok); //cnt = sscanf(line, "%ld %n", &dt, &pos); //if (cnt < 1) { if (!ok) { if (printit()) printf("bad timestep %s\n", line.latin1()); goto badresponse; } t += dt; siz = d->addValue(double(t - base), line.section(' ', 1)); } d->showValueAt(markerPos); if (siz < 0) { if (printit()) printf("overflow\n"); if (t < endRange) endRange = t; // } else if (siz > d->size - 5) { // if (t < endRange) endRange = t; } tmax = QMAX(tmax, t); if (d->update() && getArg.append) { refresh = 1; //ref } } //printf("DEBUG end line %s\n", line.latin1()); } } lastTime = tmax - base; if (markerPos == DATA_UNDEF) { setTimeLabel(lastTime); } if (!getArg.append) { compressed = line.mid(1,1) != '0'; //printf("*** compressed %d\n", compressed); } iret = gc->getLine(line); //printf("> %s\n", line.latin1()); if (iret != 0) { if (printit()) printf("missing end\n"); } // printf("*** end getCurves\n"); if (!showPlot) { showAll(); showPlot = true; } QTimer::singleShot(0, this, SLOT(replot())); return; // 1 badresponse: if (printit()) { if (iret > 0) { printf("syntax error in response %s\n", line.latin1()); } else if (iret == 0) { printf("early end of response\n"); } else if (iret == -2) { printf("timeout on sics connection\n"); } else { printf("error on response\n"); } } return; // -1 } void SeaSet::asyncHandler() { int msec; if ((msec=meas.restart()) > 100) { // printf("%d msec\n", msec); } if (async_mode == async_idle) { if (autoDo) { autoArg = autoArgDo; autoDo = false; if (autoCurvesP1()) { async_mode = async_auto; tmot.start(); } else { autoCurvesP2(); } } else if (getDo) { getArg = getArgDo; if (getCurvesP1() == 0) { async_mode = async_idle; } else { async_mode = async_get; tmot.start(); } getDo = false; if (getArg.all) { reread = false; } } else if (getNewData) { getArg.append = true; getArg.all = true; getArg.from = lastRead; getArg.to = 0; getNewData = false; // printf("*** getCurvesP1\n"); if (getCurvesP1() == 0) { async_mode = async_idle; } else { async_mode = async_get; tmot.start(); } } else if (reread) { getCurves(true, startRange, endRange); reread = false; } } if (async_mode == async_auto) { if (tmot.elapsed() > 10000) { if (printit()) printf("autoCurves timeout\n"); gc->reconnect(); tmot.start(); async_mode = async_idle; return; } if (gc->handleBuffer(0) <= 0) return; if (gc->getResponse() < 0) { if (printit()) printf("autoCurves2 timeout\n"); gc->reconnect(); async_mode = async_idle; return; } // printf("*** start autoCurvesP2\n"); autoCurvesP2(); async_mode = async_idle; } if (async_mode == async_get) { if (tmot.elapsed() > 10000) { if (printit()) printf("getCurves timeout\n"); tmot.start(); gc->reconnect(); async_mode = async_idle; return; } if (gc->handleBuffer(0) <= 0) return; if (gc->getResponse() < 0) { // printf("*** again getCurvesP1\n"); if (printit()) printf("getCurves2 timeout\n"); gc->reconnect(); async_mode = async_idle; return; } // printf("*** start getCurvesP2\n"); getCurvesP2(); async_mode = async_idle; } } void SeaSet::calcLeftLabelWidth() { SeaRow *row; labelMode = 2; // set to max mode labelWidth = 0; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { if (row->state != hiddenRow) { // force recursive calls for all labels on all plots row->plot->axis(QwtPlot::yLeft)->minimumSizeHint(); } } if (labelWidth == 0) { labelWidth = 30; } labelMode = 1; // set to cached mode } int SeaSet::leftLabelWidth(int in) { if (prt == 1) { setprintit(0); } if (labelMode == 2) { // max (calc) mode if (in > labelWidth) labelWidth = in; // printf("*** in %d\n", in); return in; } if (labelMode == 1) { // cached mode // printf("*** lwc %d\n", labelWidth + 4); return labelWidth + 4; } calcLeftLabelWidth(); labelMode = 1; // set to cached mode // printf("*** lw %d\n", labelWidth); return labelWidth; } void SeaSet::showAll() { SeaRow *row; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { if (getRowHeight(row) == 0) { saveRowHeight(row, -1); } row->state = shownRow; row->hBox->show(); row->right->show(); if (showPlot) row->plot->showAgain(); } adjustSizes(); readNewReplot(); } void SeaSet::shiftLeft() { long w; saveRange(); w = (endRange - startRange) * 3 / 4; rescale(startRange - w, endRange - w); } void SeaSet::shiftRight() { long w; saveRange(); w = (endRange - startRange) * 3 / 4; rescale(startRange + w, endRange + w); } void SeaSet::zoomOutX() { long w; time_t s, t; saveRange(); w = endRange - startRange; if (w > SEA_MAX_RANGE/2) w = SEA_MAX_RANGE/2; if (startRange <= 0) { rescaleRange(w * 2); } else { if (liveTime < base) { t = time(NULL); } else { t = liveTime; } if (endRange + w/2 > t + MAX_FUTURE) { s = t + MAX_FUTURE - w * 2; } else { s = startRange - w / 2; } rescale(s, s + w * 2); } } void SeaSet::saveRange() { undoEnabled(true); undoPlot = 0; undoStart = startRange; undoEnd = endRange; } void SeaSet::saveRange(SeaPlot *plot, QwtDoubleRect &old, bool yAuto) { saveRange(); undoPlot = plot; undoAuto = yAuto; undoRect = old; } void SeaSet::undoZoom() { if (undoPlot) { undoPlot->undoZoom(undoRect); undoPlot->autoZoom(undoAuto); } if (undoStart != 0) { rescale(undoStart, undoEnd); } undoStart = 0; undoPlot = 0; undoEnabled(false); } void SeaSet::liveUpdate() { if (live == liveOn) { if (endRange < ONE_YEAR) { endRange += lastRead; } if (startRange < ONE_YEAR) { startRange += lastRead; } if (lastRead >= endRange) { endRange += QMIN(MAX_FUTURE, (endRange - startRange)/2); getCurves(true, startRange, endRange); } else { getNewData = true; } } } void SeaSet::replot() { if (refresh <= 0) return; replotAnyway(); refresh--; if (refresh > 0) { QTimer::singleShot(0, this, SLOT(replot())); } } void SeaSet::replotAnyway() { float xrange; float tick; SeaPlot *plot; SeaRow *row; static long steps[]={30,60, 2*60,5*60,10*60,30*60, 3600,2*3600,3*3600,6*3600,24*3600, 0}; int i; double x1, x2; SeaData *d; int lw; int maxpos; double y; char txt[16], *pos; SeaCurve *crv; x1 = startRange - base; x2 = endRange - base; xrange = x2 - x1; for (i=0; steps[i] > 0; i++) { tick = steps[i]; if (xrange < tick*MAX_LABELS) break; } legendWidth = 50; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { plot = row->plot; i = plot->replotRange(x1, x2, tick); QwtPlotCurveIterator itc = plot->curveIterator(); maxpos = 0; for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { crv = (dynamic_cast(c)); if (crv) { d = crv->data; if (d->size_m > 0) { y = d->y[d->size_m-1]; snprintf(txt, sizeof(txt), "%1.5g", y); pos = strchr(txt, '.'); if (pos == 0) { pos = txt + strlen(txt); } maxpos = QMAX(maxpos, (pos - txt)); } } } for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { crv = (dynamic_cast(c)); if (crv) { d = crv->data; d->decipos = maxpos + 1; } } } lw = labelWidth; calcLeftLabelWidth(); if (labelWidth != lw) { for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { if (row->state != hiddenRow) { ((QWidget *)row->plot->axis(QwtPlot::yLeft))->updateGeometry(); } } } //layout->activate(); // printf("*** end replot\n"); if (prt) prt = 1; } void SeaSet::hideRow() { SeaRow *row; int rowN = 0, firstRow = -1, tot = 0; QValueList sizes; sizes = split->sizes(); for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { if (row->hBox->isHidden()) { tot += sizes[rowN]; sizes[rowN] = 0; } else if (firstRow < 0) { firstRow = rowN; } rowN ++; } if (firstRow < 0) { firstRow = 0; } sizes[firstRow] += tot; split->setSizes(sizes); } void SeaSet::calcBase(time_t from) { struct tm basetm; // calculate new base // printf("*** calc base %ld\n", from); if (from <= 0) { from += time(NULL); } basetm = *localtime(&from); basetm.tm_hour = 0; basetm.tm_min = 0; basetm.tm_sec = 0; base = mktime(&basetm); } void SeaSet::rescale(time_t from, time_t to) { if (to < lastRead && live == liveOn) { live = liveAuto; } if (from < startRange || to > endRange || (compressed && (from != startRange || to != endRange))) { reread = true; } startRange = from; endRange = to; if ((to > lastRead || (to >= 0 && to < SEA_MAX_RANGE)) && live == liveAuto) { live = liveOn; } replotAnyway(); if (from < base || from > base + 30*3600) { calcBase(from); reread = true; } if (reread) { getCurves(true, from, to); } else { getCurves(false, startRange, endRange); } refresh = 2; reread = false; } void SeaSet::rescaleRange(time_t range) { time_t t1, t2, now; undoStart = 0; undoPlot = 0; undoEnabled(false); now = time(NULL); t1 = - QMIN(SEA_MAX_RANGE, QMAX(60, range)); if (live != liveOff) { t2 = QMIN(MAX_FUTURE, range / 2); } else { t2 = 1; } startRange = t1 + now; endRange = t2 + now; replotAnyway(); calcBase(now - range - 4000); getCurves(true, t1, t2); startRange = t1 + lastRead; endRange = t2 + lastRead; if (live == liveAuto) live = liveOn; refresh = 2; } // called on zoom: void SeaSet::rescale(double x1, double x2) { rescale(base + (long)(x1), base + (long)(x2)); } void SeaSet::readNewReplot() { // if (live == liveAuto) live = liveOn; getCurves(false, startRange, endRange); refresh = 2; } void SeaSet::runningMarkerOff(bool beforeEvent) { // before and after a MousePress, MouseDblClick or KeyPress event // the marker is set off // markerWasOn is set accordingly // if clicked inside a plot, SeaPlotZommer will then set the marker with a MouseReleaseEvent if (markerPos == DATA_UNDEF) { markerWasOn = false; } else { markerWasOn = beforeEvent; if (!markerFix) { setMarker(DATA_UNDEF); } } } void SeaSet::setTimeLabel(double x) { QString tstr; QTime time; if (firstRow != 0) { if (x == DATA_UNDEF) { firstRow->timeLabel->setText(""); } else { time.setHMS(0,0,0); tstr = dateLabel(floor(x/(24*3600))); tstr += " " + time.addSecs(floor(x)).toString("hh:mm"); firstRow->timeLabel->setText(tstr); } } } void SeaSet::setMarker(double x) { SeaRow *row; SeaPlot *plot; if (x != DATA_UNDEF) { if (x < startRange - base) { x = startRange - base; } else if (x > endRange - base) { x = endRange - base; } } markerPos = x; for (row = rows.first(); row != 0; rows.findRef(row), row = rows.next()) { plot = row->plot; plot->setMarker(x); plot->replot(); } if (x == DATA_UNDEF) { setTimeLabel(lastTime); } else { setTimeLabel(x); } } void SeaSet::setMarker(time_t t) { if (t == 0) { setMarker(DATA_UNDEF); } else { setMarker((double)(t - base)); } } void SeaSet::gotoTime(double at, bool silent) { gotoTime(startRange, base + (long)at, endRange, silent); }