From 0fa6f49d7f09df09dd185fb306f3bedaf7db22c9 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Tue, 10 Jun 2025 11:43:29 +0200 Subject: [PATCH] most files - check if this is all we need --- Makefile_rhel7 | 386 ++++++++++ SeaClient.pro | 47 ++ command.cpp | 351 +++++++++ command.h | 51 ++ device.cpp | 1458 ++++++++++++++++++++++++++++++++++++ device.h | 267 +++++++ export.cpp | 373 ++++++++++ export.h | 39 + graph.cpp | 355 +++++++++ graph.h | 44 ++ instr_hosts.cpp | 1 + instr_hosts.h | 1 + main.cpp | 521 +++++++++++++ main.h | 26 + report.cpp | 182 +++++ report.h | 38 + seaplot.cpp | 722 ++++++++++++++++++ seaplot.h | 97 +++ seaset.cpp | 1901 +++++++++++++++++++++++++++++++++++++++++++++++ seaset.h | 228 ++++++ settings.cpp | 339 +++++++++ settings.h | 52 ++ sicsconn.cpp | 601 +++++++++++++++ sicsconn.h | 72 ++ tab.cpp | 267 +++++++ tab.h | 64 ++ utils.cpp | 462 ++++++++++++ utils.h | 59 ++ 28 files changed, 9004 insertions(+) create mode 100644 Makefile_rhel7 create mode 100644 SeaClient.pro create mode 100644 command.cpp create mode 100644 command.h create mode 100644 device.cpp create mode 100644 device.h create mode 100644 export.cpp create mode 100644 export.h create mode 100644 graph.cpp create mode 100644 graph.h create mode 120000 instr_hosts.cpp create mode 120000 instr_hosts.h create mode 100644 main.cpp create mode 100644 main.h create mode 100644 report.cpp create mode 100644 report.h create mode 100644 seaplot.cpp create mode 100644 seaplot.h create mode 100644 seaset.cpp create mode 100644 seaset.h create mode 100644 settings.cpp create mode 100644 settings.h create mode 100644 sicsconn.cpp create mode 100644 sicsconn.h create mode 100644 tab.cpp create mode 100644 tab.h create mode 100644 utils.cpp create mode 100644 utils.h diff --git a/Makefile_rhel7 b/Makefile_rhel7 new file mode 100644 index 0000000..0ec1bfc --- /dev/null +++ b/Makefile_rhel7 @@ -0,0 +1,386 @@ +############################################################################# +# Makefile for building: rhel7/SeaClient +# Generated by qmake (1.07a) (Qt 3.3.4) on: Thu Apr 6 11:05:39 2017 +# Project: SeaClient.pro +# Template: app +# Command: $(QMAKE) -o Makefile_rhel7 SeaClient.pro +############################################################################# + +####### Compiler, tools and options + +CC = gcc +CXX = g++ +LEX = flex +YACC = yacc +CFLAGS = -pipe -Wall -W -g -D_REENTRANT -DQT_THREAD_SUPPORT -DQT_SHARED +CXXFLAGS = -pipe -Wall -W -Wno-non-virtual-dtor -Wno-unused-parameter -Wno-write-strings -Wno-unused-but-set-variable -Wno-type-limits -g -D_REENTRANT -DQT_THREAD_SUPPORT -DQT_SHARED +LEXFLAGS = +YACCFLAGS= -d +INCPATH = -I/afs/psi.ch/user/z/zolliker/public/qt64/mkspecs/default -I. -I../qtx64/qt-x11-free-3.3.4/include -I../qtx64/qwt-4.2.0/include -I$(QTDIR)/include -I.ui/ -I.moc/ +LINK = g++ +LFLAGS = -Wl,-rpath,$(QTDIR)/lib +LIBS = $(SUBLIBS) -L$(QTDIR)/lib -L/usr/X11R6/lib -L/afs/psi.ch/user/z/zolliker/public/qwt64/lib -lqwt -lqt-mt -lXext -lX11 -lm -lpthread +AR = ar cqs +RANLIB = +MOC = $(QTDIR)/bin/moc +UIC = $(QTDIR)/bin/uic +QMAKE = qmake +TAR = tar -cf +GZIP = gzip -9f +COPY = cp -f +COPY_FILE= $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE= $(COPY_FILE) +INSTALL_DIR = $(COPY_DIR) +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p + +####### Output directory + +OBJECTS_DIR = rhel7/ + +####### Files + +HEADERS = main.h \ + seaset.h \ + seaplot.h \ + sicsconn.h \ + tab.h \ + settings.h \ + command.h \ + graph.h \ + utils.h \ + export.h \ + report.h \ + device.h +SOURCES = main.cpp \ + seaset.cpp \ + seaplot.cpp \ + instr_hosts.cpp \ + sicsconn.cpp \ + tab.cpp \ + settings.cpp \ + command.cpp \ + graph.cpp \ + utils.cpp \ + export.cpp \ + report.cpp \ + device.cpp +OBJECTS = rhel7/main.o \ + rhel7/seaset.o \ + rhel7/seaplot.o \ + rhel7/instr_hosts.o \ + rhel7/sicsconn.o \ + rhel7/tab.o \ + rhel7/settings.o \ + rhel7/command.o \ + rhel7/graph.o \ + rhel7/utils.o \ + rhel7/export.o \ + rhel7/report.o \ + rhel7/device.o +FORMS = +UICDECLS = +UICIMPLS = +SRCMOC = .moc/moc_main.cpp \ + .moc/moc_seaset.cpp \ + .moc/moc_seaplot.cpp \ + .moc/moc_sicsconn.cpp \ + .moc/moc_tab.cpp \ + .moc/moc_settings.cpp \ + .moc/moc_command.cpp \ + .moc/moc_graph.cpp \ + .moc/moc_utils.cpp \ + .moc/moc_export.cpp \ + .moc/moc_report.cpp \ + .moc/moc_device.cpp +OBJMOC = rhel7/moc_main.o \ + rhel7/moc_seaset.o \ + rhel7/moc_seaplot.o \ + rhel7/moc_sicsconn.o \ + rhel7/moc_tab.o \ + rhel7/moc_settings.o \ + rhel7/moc_command.o \ + rhel7/moc_graph.o \ + rhel7/moc_utils.o \ + rhel7/moc_export.o \ + rhel7/moc_report.o \ + rhel7/moc_device.o +DIST = SeaClient.pro +QMAKE_TARGET = SeaClient +DESTDIR = rhel7/ +TARGET = rhel7/SeaClient + +first: all +####### Implicit rules + +.SUFFIXES: .c .o .cpp .cc .cxx .C + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.cc.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.cxx.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.C.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< + +####### Build rules + +all: Makefile_rhel7 $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + test -d rhel7/ || mkdir -p rhel7/ + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(OBJCOMP) $(LIBS) + +mocables: $(SRCMOC) +uicables: $(UICDECLS) $(UICIMPLS) + +$(MOC): + ( cd $(QTDIR)/src/moc && $(MAKE) ) + +Makefile_rhel7: SeaClient.pro /afs/psi.ch/user/z/zolliker/public/qt64/mkspecs/default/qmake.conf ../qt64/lib/libqt-mt.prl + $(QMAKE) -o Makefile_rhel7 SeaClient.pro +qmake: + @$(QMAKE) -o Makefile_rhel7 SeaClient.pro + +dist: + @mkdir -p rhel7/SeaClient && $(COPY_FILE) --parents $(SOURCES) $(HEADERS) $(FORMS) $(DIST) rhel7/SeaClient/ && ( cd `dirname rhel7/SeaClient` && $(TAR) SeaClient.tar SeaClient && $(GZIP) SeaClient.tar ) && $(MOVE) `dirname rhel7/SeaClient`/SeaClient.tar.gz . && $(DEL_FILE) -r rhel7/SeaClient + +mocclean: + -$(DEL_FILE) $(OBJMOC) + -$(DEL_FILE) $(SRCMOC) + +uiclean: + +yaccclean: +lexclean: +clean: mocclean + -$(DEL_FILE) $(OBJECTS) + -$(DEL_FILE) *~ core *.core + + +####### Sub-libraries + +distclean: clean + -$(DEL_FILE) rhel7/$(TARGET) $(TARGET) + + +FORCE: + +####### Compile + +rhel7/main.o: main.cpp main.h \ + instr_hosts.h \ + tab.h \ + graph.h \ + settings.h \ + device.h \ + export.h \ + report.h \ + seaset.h \ + sicsconn.h \ + seaplot.h \ + command.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/main.o main.cpp + +rhel7/seaset.o: seaset.cpp seaset.h \ + utils.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/seaset.o seaset.cpp + +rhel7/seaplot.o: seaplot.cpp seaplot.h \ + utils.h \ + seaset.h \ + sicsconn.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/seaplot.o seaplot.cpp + +rhel7/instr_hosts.o: instr_hosts.cpp instr_hosts.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/instr_hosts.o instr_hosts.cpp + +rhel7/sicsconn.o: sicsconn.cpp sicsconn.h \ + instr_hosts.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/sicsconn.o sicsconn.cpp + +rhel7/tab.o: tab.cpp tab.h \ + graph.h \ + settings.h \ + device.h \ + export.h \ + report.h \ + seaset.h \ + sicsconn.h \ + seaplot.h \ + command.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/tab.o tab.cpp + +rhel7/settings.o: settings.cpp settings.h \ + utils.h \ + seaset.h \ + device.h \ + sicsconn.h \ + seaplot.h \ + command.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/settings.o settings.cpp + +rhel7/command.o: command.cpp command.h \ + utils.h \ + sicsconn.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/command.o command.cpp + +rhel7/graph.o: graph.cpp graph.h \ + seaset.h \ + utils.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/graph.o graph.cpp + +rhel7/utils.o: utils.cpp utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/utils.o utils.cpp + +rhel7/export.o: export.cpp export.h \ + seaset.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/export.o export.cpp + +rhel7/report.o: report.cpp report.h \ + sicsconn.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/report.o report.cpp + +rhel7/device.o: device.cpp device.h \ + utils.h \ + seaset.h \ + command.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/device.o device.cpp + +rhel7/moc_main.o: .moc/moc_main.cpp main.h tab.h \ + graph.h \ + settings.h \ + device.h \ + export.h \ + report.h \ + seaset.h \ + sicsconn.h \ + seaplot.h \ + command.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_main.o .moc/moc_main.cpp + +rhel7/moc_seaset.o: .moc/moc_seaset.cpp seaset.h sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_seaset.o .moc/moc_seaset.cpp + +rhel7/moc_seaplot.o: .moc/moc_seaplot.cpp seaplot.h seaset.h \ + sicsconn.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_seaplot.o .moc/moc_seaplot.cpp + +rhel7/moc_sicsconn.o: .moc/moc_sicsconn.cpp sicsconn.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_sicsconn.o .moc/moc_sicsconn.cpp + +rhel7/moc_tab.o: .moc/moc_tab.cpp tab.h graph.h \ + settings.h \ + device.h \ + export.h \ + report.h \ + seaset.h \ + sicsconn.h \ + seaplot.h \ + command.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_tab.o .moc/moc_tab.cpp + +rhel7/moc_settings.o: .moc/moc_settings.cpp settings.h seaset.h \ + device.h \ + sicsconn.h \ + seaplot.h \ + command.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_settings.o .moc/moc_settings.cpp + +rhel7/moc_command.o: .moc/moc_command.cpp command.h sicsconn.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_command.o .moc/moc_command.cpp + +rhel7/moc_graph.o: .moc/moc_graph.cpp graph.h seaset.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_graph.o .moc/moc_graph.cpp + +rhel7/moc_utils.o: .moc/moc_utils.cpp utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_utils.o .moc/moc_utils.cpp + +rhel7/moc_export.o: .moc/moc_export.cpp export.h seaset.h \ + sicsconn.h \ + seaplot.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_export.o .moc/moc_export.cpp + +rhel7/moc_report.o: .moc/moc_report.cpp report.h sicsconn.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_report.o .moc/moc_report.cpp + +rhel7/moc_device.o: .moc/moc_device.cpp device.h seaset.h \ + command.h \ + sicsconn.h \ + seaplot.h \ + utils.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o rhel7/moc_device.o .moc/moc_device.cpp + +.moc/moc_main.cpp: $(MOC) main.h + $(MOC) main.h -o .moc/moc_main.cpp + +.moc/moc_seaset.cpp: $(MOC) seaset.h + $(MOC) seaset.h -o .moc/moc_seaset.cpp + +.moc/moc_seaplot.cpp: $(MOC) seaplot.h + $(MOC) seaplot.h -o .moc/moc_seaplot.cpp + +.moc/moc_sicsconn.cpp: $(MOC) sicsconn.h + $(MOC) sicsconn.h -o .moc/moc_sicsconn.cpp + +.moc/moc_tab.cpp: $(MOC) tab.h + $(MOC) tab.h -o .moc/moc_tab.cpp + +.moc/moc_settings.cpp: $(MOC) settings.h + $(MOC) settings.h -o .moc/moc_settings.cpp + +.moc/moc_command.cpp: $(MOC) command.h + $(MOC) command.h -o .moc/moc_command.cpp + +.moc/moc_graph.cpp: $(MOC) graph.h + $(MOC) graph.h -o .moc/moc_graph.cpp + +.moc/moc_utils.cpp: $(MOC) utils.h + $(MOC) utils.h -o .moc/moc_utils.cpp + +.moc/moc_export.cpp: $(MOC) export.h + $(MOC) export.h -o .moc/moc_export.cpp + +.moc/moc_report.cpp: $(MOC) report.h + $(MOC) report.h -o .moc/moc_report.cpp + +.moc/moc_device.cpp: $(MOC) device.h + $(MOC) device.h -o .moc/moc_device.cpp + +####### Install + +install: + +uninstall: + diff --git a/SeaClient.pro b/SeaClient.pro new file mode 100644 index 0000000..24f417a --- /dev/null +++ b/SeaClient.pro @@ -0,0 +1,47 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt-mt warn_on thread debug static + +QMAKE_CXXFLAGS_WARN_ON += -Wno-non-virtual-dtor -Wno-unused-parameter -Wno-write-strings \ + -Wno-unused-but-set-variable -Wno-type-limits + +LIBS += -L$$(QWTDIR)/lib -lqwt + +INCLUDEPATH += $$(QTDIR)/include +INCLUDEPATH += $$(QWTDIR)/include + +HEADERS += main.h \ + seaset.h \ + seaplot.h \ + sicsconn.h \ + tab.h \ + settings.h \ + command.h \ + graph.h \ + utils.h \ + export.h \ + report.h \ + device.h + +SOURCES += main.cpp \ + seaset.cpp \ + seaplot.cpp \ + instr_hosts.cpp \ + sicsconn.cpp \ + tab.cpp \ + settings.cpp \ + command.cpp \ + graph.cpp \ + utils.cpp \ + export.cpp \ + report.cpp \ + device.cpp + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = $$(linuxsys) + DESTDIR = $$(linuxsys) +} + diff --git a/command.cpp b/command.cpp new file mode 100644 index 0000000..4f078fa --- /dev/null +++ b/command.cpp @@ -0,0 +1,351 @@ +#include "command.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +class CmdLine : public QLineEdit { +public: + CmdLine(QWidget *parent, const char *name) : QLineEdit(parent, name), history(""), edit("") { + p = history.end(); + atEnd = true; + } + void keyPressEvent(QKeyEvent *e) { + int key = e->key(); + if (key == Qt::Key_Up) { + if (p == history.end()) { + if (text().isEmpty()) { + if (!edit.isEmpty()) { + setText(edit); + return; + } + } else { + edit = text(); + } + } + if (p != history.begin()) { + --p; + setText(*p); + } + } else if (key == Qt::Key_Down) { + if (p != history.end()) { + p++; + } + if (p == history.end()) { + if (edit.compare(text()) == 0) { + setText(""); + } else { + setText(edit); + } + } else { + setText(*p); + } + } else if (key == Qt::Key_Return || key == Qt::Key_Enter) { + edit=""; + QLineEdit::keyPressEvent(e); + } else { + QLineEdit::keyPressEvent(e); + if (p == history.end()) { + edit = text(); + } + } + } + void addToHistory(const QString &txt) { + p = history.find(txt); + if (p != history.end()) { + history.remove(p); + } + history.append(txt); + p = history.end(); + while (history.count() > 256) { + history.remove(history.begin()); + } + } +private: + QStringList history; + QStringList::Iterator p; + QString edit; + bool atEnd; +}; + +Command::Command(QWidget * parent, const char *name, SicsConnection *initConn) + : QWidget(parent, name, 0) +{ + //QGridLayout *grid; + QVBoxLayout *vbox; + QFont monospace("Courier"); + + getHistory = true; + //grid = new QGridLayout(this, 2, 2, 4, 4, "grid"); + vbox = new QVBoxLayout(this, 4, 4, "cmdBox"); + + logStartupState = 1; + + log = new MyTextEdit(this, "log"); + log->setReadOnly(TRUE); + //add_to_log(" "); + log->setBuffered(true); + + vbox->addWidget(new QLabel("Input/Output History:", this)); + vbox->addWidget(log); + ownCommand = false; + quiet = false; + + vbox->addWidget(new QLabel("Command Input:", this)); + monospace.setStyleHint(QFont::TypeWriter); + cmd = new CmdLine(this, "cmd"); + cmd->setFont(monospace); + vbox->addWidget(cmd,3,0); + clearWState(WState_Polished); + conn=0; + cmd->setFocusPolicy(StrongFocus); + setConn(initConn); + scrollH = 0; + QTimer *timer = new QTimer(this, "repeat"); + connect(timer, SIGNAL(timeout()), this, SLOT(handler())); + timer->start(10); + checktmo = false; + + /* + dirtyLog = 0; + + QTimer *logTimer = new QTimer(this); + connect(logTimer, SIGNAL(timeout()), SLOT(showLog())); + logTimer->start(50); + + connect(log, SIGNAL(contentsMoving(int, int)), this, SLOT(scrolling(int, int))); + */ +} + +void Command::showIt(bool yes) { + setShown(yes); +} + +void Command::showText() { + printf("%s\n", log->text().latin1()); +} + +int str_beg(const char *text, const char *name) { + return (strncmp(text, name, strlen(name)) == 0); +} + +void Command::add_to_log(QString line, Style style) { +/* + QFont f = font(); + static bool x=true; + if (x) { + printf("family %s size %d %d s %d\n", f.family().latin1(), f.pointSize(), f.pixelSize(), f.styleStrategy()); + x = false; + } +*/ + if (ownCommand) { + log->appendHtml(""); + } + switch (style) { + case style_command: log->appendHtml(""); break; + case style_error: log->appendHtml(""); break; + case style_normal: break; + } + log->appendText(line); + if (style > 0) { + log->appendHtml(""); + } + if (ownCommand) { + log->appendHtml(""); + } + log->appendText("\n"); +} + +/* +void Command::showLog(void) { + if (dirtyLog) { + dirtyLog = 0; + log->setText(logText); + log->scrollToBottom(); + } +} + +void Command::scrolling(int x, int y) { + scrolled = true; +} +*/ + +void Command::handleSics(const char * text, bool *done) { + if (str_beg(text, "TRANSACTIONFINISHED")) { + if (logStartupState) { + log->setBuffered(false); + } + ownCommand = false; + if (done) *done = false; + return; + } + if (str_beg(text, "Deleting connection") || + str_beg(text, "Accepted connection") || + (str_beg(text, "User ") && strstr(text, " privilege") != 0) || + str_beg(text, "Change of Authorisation") || + str_beg(text, "fulltransact config ") || + str_beg(text, "UserRights =") || + str_beg(text, "fulltransact status") || + (str_beg(text, "OK") && strlen(text) <= 3) || + str_beg(text, "fulltransact commandlog tail") || + str_beg(text, "Login OK")) { + if (done) *done = false; + return; + } + + activate(); + if (str_beg(text, "fulltransact") || str_beg(text, "transact") || str_beg(text, "TRANSACTIONSTART")) { + if (str_beg(text, "TRANSACTIONSTART config listen")) { + if (done) *done = false; + return; + } + if (text[0] == 'f') { + text += 13; + } else if (text[0] == 'T') { + text += 17; + ownCommand = true; + } else { + text += 8; + } +// if (!blankLine) { +// add_to_log(" "); +// } + if (strcmp(text, " ") > 0) { + add_to_log(" "); + if (timeStamp != "") { + add_to_log(timeStamp); + timeStamp = ""; + } + add_to_log(text, style_command); +// printf("*** BB {%s}\n", text); + if (getHistory) { + ((CmdLine *)cmd)->addToHistory(text); + } + } + } else if (strstr(text, "transAct") == text || strstr(text, "fulltransAct") == text) { // hide these commands + // quiet = true; + return; + } else if (strstr(text, "ERROR:")) { + add_to_log(text, style_error); +// printf("*** RR %s\n", text); + } else if (quiet) { + quiet = false; /* swallow only one line. this is for nicos read -> frappy read -> sea spam*/ + } else { + if (strcmp(text, " ") > 0) { + if (str_beg(text, "===")) { + timeStamp = text; + } else { + if (timeStamp != "") { + add_to_log(timeStamp); + timeStamp = ""; + } + add_to_log(text); + } +// printf("*** N %s %s %s\n", text); + } + } + if (done) *done = true; + return; +} + +void Command::handler() { + int iret; + + iret = conn->handleBuffer(0); + if (iret == -2) { // connect_error + // printf("*** reconnect\n"); + conn->reconnect(); + if (getCommandLog == 0) { + getCommandLog = 1; /* force config listen to be done on the new connection */ + } + } else if (iret > 0) { // success + checktmo = false; + if (getCommandLog == 2) { + conn->sendCommand("commandlog tail 999"); + getCommandLog = 1; + } else if (getCommandLog == 1) { + conn->getResponse(); + getCommandLog = 0; + conn->sendCommand("config listen 1"); + // printf("*** config listen\n"); + } else if (!cmdbuffer.isEmpty()) { + if (sendCmd1(cmdbuffer.first().latin1())) { + tmot.start(); + checktmo = true; + cmdbuffer.pop_front(); + } + } + } else if (checktmo && tmot.elapsed() > 10000) { + tmot.start(); + // printf("*** timeout\n"); + handleSics("ERROR: timeout -> reconnect\n"); + conn->reconnect(); + getCommandLog = 1; /* force config listen to be done on the new connection */ + } + + if (height() < scrollH) { + printf("%d\n", scrollH); + log->scrollToBottom(); + } + scrollH = height(); + if (conn) { + conn->handleMessages(0); + } +} + +void Command::sendCmd(const char *command) { + QString qs(command); + cmdbuffer.append(qs); + ((CmdLine *)cmd)->addToHistory(command); +} + +int Command::sendCmd1(const char *command) { + int iret; + QString line; + QString cCmd; + + iret = conn->sendCommand(command); + if (iret < 0) { + handleSics("ERROR: connection failed\n"); + return 0; + } +// if (!blankLine) { +// add_to_log(" "); +// } +// add_to_log(command, style_command); + cmd->setText(""); + log->scrollToBottom(); + + while ((iret=conn->getLine(line)) > 0) { + handleSics(line.latin1()); + } +// add_to_log(" "); +// blankLine = true; + log->scrollToBottom(); + return 1; +} + +void Command::handleCmd() { + if (getHistory) { + getHistory = false; + } + sendCmd(cmd->text().latin1()); +} + +void Command::setConn(SicsConnection *conn) { + assert(conn); + if (this->conn != conn) { + this->conn = conn; + connect(conn, SIGNAL(handle(const char *, bool *)), this, SLOT(handleSics(const char *, bool *))); + connect(cmd, SIGNAL(returnPressed()), this, SLOT(handleCmd())); + getCommandLog = 2; + } + getHistory = true; +} diff --git a/command.h b/command.h new file mode 100644 index 0000000..28e2327 --- /dev/null +++ b/command.h @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include "sicsconn.h" +#include "utils.h" + +class QCheckBox; + +class Command : public QWidget +{ + Q_OBJECT +public: + Command(QWidget *parent, const char *name, SicsConnection *conn); + void setConn(SicsConnection *conn); + void showText(); + QLineEdit *cmd; + MyTextEdit *log; + enum Style {style_normal, style_command, style_error}; + +signals: + void activate(); + +public slots: + void handleSics(const char *text, bool *done = NULL); + void sendCmd(const char *cmd); + void handleCmd(); + void handler(); + void showIt(bool show); +// void showLog(); +// void scrolling(int x, int y); + +private: + void add_to_log(QString text, Style style = style_normal); + int sendCmd1(const char *cmd); + + SicsConnection *conn; + int scrollH; + bool getHistory; + bool ownCommand; + bool quiet; /* for suppressing 'spam' when reading values from nicos/frappy */ + bool blankLine; + QString timeStamp; + int getCommandLog; + QStringList cmdbuffer; + QTime tmot; + bool checktmo; + int logStartupState; +// bool dirtyLog; +}; diff --git a/device.cpp b/device.cpp new file mode 100644 index 0000000..33482e3 --- /dev/null +++ b/device.cpp @@ -0,0 +1,1458 @@ +#include "device.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +static const char *rPix[] = { + "10 10 2 1", + " c None", + "X c #0000CC", + "XX ", + "XXXX ", + "XXXXXX ", + "XXXXXXXX ", + "XXXXXXXXXX", + "XXXXXXXXXX", + "XXXXXXXX ", + "XXXXXX ", + "XXXX ", + "XX ", +}; + +static const char *dPix[] = { + "10 10 2 1", + " c None", + "X c #0000CC", + "XXXXXXXXXX", + "XXXXXXXXXX", + " XXXXXXXX ", + " XXXXXXXX ", + " XXXXXX ", + " XXXXXX ", + " XXXX ", + " XXXX ", + " XX ", + " XX ", +}; + +/* +static const char *grPix[] = { + "16 12 4 1", + " c None", + "/ c #CC0000", + "- c #0000CC", + "X c #000000", + "XXXXXXXXXXXXXXXX", + "X X", + "X X", + "X - X", + "X ---- X", + "X -- ----- X", + "X ---- ----X", + "X----- -X", + "X-- X", + "X X", + "X X", + "XXXXXXXXXXXXXXXX", +}; + +static const char *gnPix[] = { + "16 12 4 1", + " c None", + "/ c #CC0000", + "- c #0000CC", + "X c #000000", + "X//XXXXXXXXXX//X", + "X // // X", + "X // // X", + "X // - // X", + "X //--// X", + "X ////---- X", + "X ----// ----X", + "X-----//// -X", + "X-- // // X", + "X // // X", + "X // // X", + "XX//XXXXXXXX//XX", +}; +*/ + +static int lineEditFrameWidth = 0; + +LineInput::LineInput(QWidget *parent, QString &labelTxt, const char *var) : QHBox(parent, var), value("") { + QBoxLayout *l = dynamic_cast(layout()); + + l->setAutoAdd(false); + + label = new QLabel(labelTxt, this); + label->setAlignment(Qt::AlignRight); + l->addWidget(label); + + le = new QLineEdit(this, var); + connect(le, SIGNAL(returnPressed()), this, SLOT(handleReturn())); + connect(le, SIGNAL(lostFocus()), this, SLOT(focusLost())); + QFont monospace("Courier"); + monospace.setStyleHint(QFont::TypeWriter); + le->setFont(monospace); + l->addWidget(le); + + l->setSpacing(6); +} + +void LineInput::setLength(int l) { + QFontMetrics fm = le->fontMetrics(); + if (l==0) { + l = 9; + } + if (l <= 9) { + le->setAlignment(Qt::AlignRight); + } else { + le->setAlignment(Qt::AlignLeft); + } + lineEditFrameWidth = le->frameWidth(); + le->setFixedWidth(fm.width("0") * l + 2 * lineEditFrameWidth + 4); +} + +int LineInput::stdWidth() { + QBoxLayout *l = dynamic_cast(layout()); + setLength(0); + l->activate(); + return label->width() + l->spacing() + 2 * l->margin() + le->width(); +} + +void LineInput::handleReturn() { + QString c(name()); + if (le->text().isEmpty()) { + c.append(" \"\""); + } else { + c.append(" "); + c.append(le->text()); + } + sendCmd(c.latin1()); + value = le->text(); + changed(); +} + +void LineInput::focusLost() { + if (value.compare(le->text()) != 0) { + handleReturn(); + } +} + +void LineInput::setValue(const QString &value) { + if ((this->value.compare(le->text()) == 0) && this->value.compare(value) != 0) { + le->setText(value); + this->value = value; + } +} + +void LineInput::setEraseColor(const QColor & color) { + QHBox::setEraseColor(color); + label->setEraseColor(color); + label->setEraseColor(color); +} + +RdOnly::RdOnly(QWidget *parent, QString &labelTxt, QString &val, const char *var) : QHBox(parent, var) { + QBoxLayout *l = dynamic_cast(layout()); + + l->setAutoAdd(false); + label = new QLabel(labelTxt, this); + value = new QLabel(val, this); + QFont monospace("Courier"); + monospace.setStyleHint(QFont::TypeWriter); + value->setFont(monospace); + label->setAlignment(Qt::AlignRight); + value->setAlignment(Qt::AlignRight); + l->addWidget(label); + l->addWidget(value); + l->setSpacing(6); + len = 9; +} + +void RdOnly::setLength(int l) { + QFontMetrics fm = value->fontMetrics(); + if (l==0) { + l = 9; + } + len = l; + if (l <= 9) { + value->setAlignment(Qt::AlignRight); + } else { + value->setAlignment(Qt::AlignLeft); + } + value->setFixedWidth(fm.width("0") * l + 2 * lineEditFrameWidth + 4); +} + +void RdOnly::setValue(const QString &val) { + bool ok; + double f; + int p, i; + QString v, ve; + + if (val.length() <= len) { + // length is ok + value->setText(val); + return; + } + f = val.stripWhiteSpace().toDouble(&ok); + if (!ok) { + // it's not a number + value->setText(val); + return; + } + // try to reduce the length by reducing the precision + for (p = 6; p > 0; p--) { + v.setNum(f, 'g', p); + i = v.find('e'); + if (i >= 0) { + i++; + if (v[i] == '-') { + i++; + } else if (v[i] == '+') { + // skip + in exponent + v.remove(i,1); + } + if (v[i] == '0') { + // skip leading 0 in exponent + v.remove(i,1); + } + } + if (v.length() <= len) { + value->setText(v); + return; + } + } + value->setText(v); +} + +CheckBox::CheckBox(const QString &label, QWidget *parent, const char *var) + : QCheckBox(label, parent, var) { + connect(this, SIGNAL(clicked()), this, SLOT(handleReturn())); +} + +void CheckBox::handleReturn() { + QString c(name()); + if (isOn()) { + c.append(" 1"); + } else { + c.append(" 0"); + } + sendCmd(c.latin1()); + changed(); +} + +void CheckBox::setValue(const QString &value) { + if (value[0] == '0' || value[0] <= ' ') { + if (isOn()) { + setChecked(false); + } + } else { + if (!isOn()) { + setChecked(true); + } + } +} + +ClickButton::ClickButton(QWidget *parent, const char *var) + : QPushButton(parent, var) { + dx = 0; +} + +void ClickButton::handleReturn() { + setFocus(); + sendCmd(name()); + changed(); +} + +void ClickButton::setLabel(const QString &label) { + QFontMetrics fm = fontMetrics(); + if (dx == 0) { + setText("BUTTON"); + dx = (width() - fm.width("BUTTON")) / 3; + } + setText(label); + setMaximumWidth(fm.width(text()) + dx); +} + +RadioButton::RadioButton(const QString &label, QWidget *parent, + const QString &cmdArg, const char *value) : QRadioButton(label, parent, value) +{ + cmd = cmdArg; + connect(this, SIGNAL(clicked()), this , SLOT(handleClick())); +} + +void RadioButton::handleClick() { + QString c(cmd); + c.append(" "); + c.append(name()); + sendCmd(c.latin1()); + clearOthers(name()); + changed(); + setFocus(); +} + +void RadioButton::setValue(const QString &value) { + setChecked(value.compare(name()) == 0); +} + +void ColorMenu::init() { + const QPixmap *pixmap; + int i; + + setEditable(true); + listBox()->setRowMode(QListBox::FitToHeight); + insertItem(QString("auto"), 0); + for (i=1; i < 16; i++) { + pixmap = thisPixmap(i); + if (pixmap == NULL) { + i = -1; + break; + } + insertItem(*pixmap, i); + } + connect(this, SIGNAL(activated(int)), + this, SLOT(handleAct(int))); +} + +ColorMenu::ColorMenu(QString &color, QWidget *parent) + : QComboBox(parent, "settings") +{ + init(); + setCurrentItem(0); + setCurrentText(color); +} + +ColorMenu::ColorMenu(QWidget *parent, const char *var) + : QComboBox(parent, var) +{ + init(); + nochange = true; + setCurrentItem(0); + setCurrentText(""); + nochange = false; + setLength(0); + value = ""; + connect(this, SIGNAL(textChanged(const QString &)), + this, SLOT(handleChange(const QString &))); +} + +void ColorMenu::handleAct(int index) { + QString text; + +// printf("*** handleAct %d\n", index); + if (index > 0 && index < 16) { + Convert2ColorName(index, text); + nochange = true; + setCurrentItem(0); + nochange = false; + value = text; + setCurrentText(text); + } +// printf("*** handleAct end\n"); +} + +void ColorMenu::setValue(const QString &text) { + if (value.compare(currentText()) == 0 && value.compare(text) != 0) { +// printf("*** setValue %s -> %s\n", value.latin1(), text.latin1()); + nochange = true; + setCurrentItem(0); + value = text; + setCurrentText(text); + nochange = false; + } +} + +void ColorMenu::handleChange(const QString &text) { + QString c(name()); + int index; + +// printf("*** nochange %d empty %d\n", nochange, (text == "")); + if (nochange || text == "") return; + index = Convert2ColorIndex(text); + if (index < -1) return; // not a valid text + value = text; +// printf("*** handleChange %s\n", text.latin1()); + c.append(" "); + c.append(text); + sendCmd(c.latin1()); + changed(); +} + +int ColorMenu::getColorIndex() { + int index = Convert2ColorIndex(currentText()); + if (index < -1) { + index = -1; + } + return index; +} + +void ColorMenu::setLength(int min, int max) { + QFontMetrics fm = fontMetrics(); + if (min == 0) { + min = 10; + } + if (max < min) { + max = min; + } + setMinimumWidth(fm.width("0") * min + 8); + setMaximumWidth(fm.width("0") * max + 8); +} + +Menu::Menu(const QString &label, QWidget *parent, const char *var) + : QComboBox(parent, var) +{ + connect(this, SIGNAL(activated(const QString &)), + this, SLOT(handleAct(const QString &))); +} + +void Menu::handleAct(const QString &value) { + QString c(name()); + + c.append(" "); + c.append(value); + sendCmd(c.latin1()); + changed(); +} + +void Menu::setValue(const QString &value) { + setCurrentText(value); +} + +/* +GraphButton::GraphButton(QWidget *parent, const char *name) + : QPushButton(parent, name) +{ + setFlat(true); + setPixmap(QPixmap(grPix)); + connect(this, SIGNAL(clicked()), this, SLOT(handleClick())); + setFixedSize(24,20); + hide(); +} + +void GraphButton::handleClick() { + selectItem(name()); + changed(); +} +*/ + +bool PathStartsWith(QString &path, const char *name) { + if (path.startsWith(name)) { + int l = strlen(name); +// if (path.length(name) == l || path[l] == '/') return true; + if (path[l] == 0 || path[l] == '/') return true; + } + return false; +} + +void Group::init() { + indent = 0; + isSelectMenu = false; + colDiv = 1; +} + +Group::Group(Device *p, const char *name, bool selectMenu) + : QVBox(p->viewport(), name) + , items(101,false) +{ + //title = new QLabel("Title", this); + //boxLayout->addWidget(title); + parentGroup = 0; + device = p; + init(); + arrow = 0; + shown=true; + title = 0; + isSelectMenu = selectMenu; + autoclose = false; +} + +Group::Group(Group *p, const QString &tit, const char *name) + : QVBox(p->device->viewport(), name) + , items(101,false) +{ + QFont bold; + QBoxLayout *lay; + + parentGroup = p; + device = p->device; + init(); + isSelectMenu = p->isSelectMenu; + if (tit.isEmpty()) { + arrow = 0; + shown = true; + } else { + title = new QLabel(tit, this); + lay = dynamic_cast(layout()); + lay->setAutoAdd(false); + lay->add(title); + lay->addStretch(1); + setFrameStyle(QFrame::MenuBarPanel); + + indent = p->indent + 20; + arrow = new QPushButton(this, "arrow"); + arrow->setFlat(true); + SetEraseColor(arrow, topLevelWidget(), true); + arrow->setPixmap(QPixmap(rPix)); + arrow->setFixedSize(16, 16); + bold = font(); + bold.setWeight(75); + setFont(bold); + connect(arrow, SIGNAL(clicked()), this, SLOT(toggle())); + shown=false; + } +} + +void Group::showIt(bool yes) { + shown = yes; + if (arrow) { // && arrow->isShown()) { + if (shown) { + arrow->setPixmap(QPixmap(dPix)); + } else { + arrow->setPixmap(QPixmap(rPix)); + } + } +} + +void Group::newline() { + device->x = indent; + device->y = device->nextY; +} + +void Group::add(Item *item, QWidget *w) { + int frac, offs, hei, wid; + QFont font; + + if (w == 0) w = item->w; + if (wStyle != item->wStyle) { + switch (item->wStyle) { + case 0: + item->normalFont = w->font(); + case 'W': // remove warning style + case 'A': // remove warning style +// case 'G': // remove graph settings style + w->setEraseColor(eraseColor()); + break; + case 'H': // remove header style + w->setFont(item->normalFont); + } + switch (wStyle) { + case 'W': // warning: yellow background + w->setEraseColor(QColor(255,255,0)); + break; + case 'A': // warning: orange background + w->setEraseColor(QColor(255,127,0)); + break; +// case 'G': // graph settings: grey background +// w->setEraseColor(QColor(150,150,150)); +// break; + case 'H': // header style + font = item->normalFont; + font.setWeight(QFont::Normal); + if (font.pointSize() > 0) { + font.setPointSize(font.pointSize() * 6 / 4); + } else { + font.setPixelSize(font.pixelSize() * 6 / 4); + } + w->setFont(font); + } + item->wStyle = wStyle; + } + wStyle = 0; + //printf("%s: add %s/%s %d\n", name(), w->className(), w->name(), sameRow); + if (!sameRow) { + frac = (device->x - indent) % (device->columnWidth / colDiv); + device->x -= frac; + if (frac > device->xspace / 2) { + device->x += device->columnWidth / colDiv; + } + } + if (item->w->layout()) { + item->w->layout()->activate(); + } + w->resize(w->sizeHint()); + wid = w->width(); + hei = w->height(); + if (device->x + wid > device->visibleWidth()) { + newline(); + } + device->x += device->xspace; + if (hei >= device->lineHeight) { + offs = 0; + device->nextY = QMAX(device->nextY, device->y + hei + device->yspace); + } else { + offs = (device->lineHeight - hei) / 2; + device->nextY = QMAX(device->nextY, device->y + device->lineHeight + device->yspace); + } + device->addChild(item->w, device->x, device->y + offs); + device->x += wid; + device->maxWid = QMAX(device->maxWid, device->x); + item->w->show(); + item->used = true; + sameRow = false; + tight = false; + if (!tip.isEmpty()) { + QToolTip::add(item->w, tip); + tip=""; + } +} + +void Group::initItem(Item *item, QWidget *w, const char *line, bool activate) { + if (activate) { + connect(w, SIGNAL(sendCmd(const char *)), device->com, SLOT(sendCmd(const char *))); + connect(w, SIGNAL(changed()), device, SLOT(update())); + } + if (item->w) delete item->w; + item->w = w; + items.replace(line, item); +} + +void Group::toggle() { + if (shown) { + if (autoclose) { + autocloseAll(); // close containing groups + } else { + } + autoclose = false; + } + showIt(!shown); + device->update(false); +} + +Item *Group::findItem(const char *name) { + Item *item; + item = items.find(name); + if (!item) { + item = new Item(); + item->grp = this; + } + return item; +} + +void Group::hideContents() { + Item *item; + Group *g; + + for (QDictIterator it(items); (item = it.current()); ++it) { + if (item->used) { + g = dynamic_cast(item->w); + if (g) { + if (g->shown) { + g->hideContents(); + } + g->arrow->hide(); + } + item->w->hide(); + /* + if (item->graphButton) { + item->graphButton->hide(); + } + */ + } + } +} + +void Group::updateLayout(QStringList::Iterator &sit) { + QString lineQ; + int lwid=0; + LineInput *lineInp; + CheckBox *cb; + QLabel *label; + RdOnly *rdonly; + Menu *menu=0; + ColorMenu *colorMenu=0; + ClickButton *pb; + QString tit(""); + QPtrStack stack; + Group *grp; + QString value; + const char *line; + Item *item; + Item *rightItem = 0; + int labelNum=0; + char labelCode[8]; + const char *arg; + RadioButton *rb; + QString radioName(""); + QString radioLine(""); + int i; + int menuItem; + int yline; + bool autocloseOnRadio = false; + // obsolete graph: char groupName[256]; + + device->x = indent; + tip=""; + sameRow = false; + tight = false; + wStyle = 0; + + for (QDictIterator it(items); (item = it.current()); ++it) { + item->used = false; + } + for (; sit != device->code.end(); ++sit) { + lineQ = (*sit); + line = lineQ.latin1(); + if (line[0] != '-') { + if (line[0]) { + if (printit()) printf("%s\n", line); + } + continue; + } + arg = line+2; + //printf("L %s\n", line); + switch (line[1]) { + case '-': // no line break for next item + tight = true; + if (line[2] != '-') { + sameRow = true; + } + break; + case 'P': // line break + newline(); + break; + case 'W': // custom width + lwid = atoi(arg); + break; + case 'T': // group title + tit = arg; + break; + case 'H': // help (tool tip) + if (!tip.isEmpty()) { + tip.append("\n"); + } + tip.append(arg); + break; + case '>': // add last item to group title line (experimental) + rightItem = item; + break; + /* + case 's': // auto open (for stick group), will be obsolete + if (device->openGroups == "") { + device->openGroups = "sticks"; + device->update(); + } + break; + */ + case 'G': // group + item = findItem(line); + grp = dynamic_cast(item->w); + if (grp == 0) { + grp = new Group(this, tit, arg); + initItem(item, grp, line); + } else { + grp->title->setText(tit); + } + if (device->openGroup != "" && PathStartsWith(device->openGroup, arg)) { + grp->autoclose = false; + if (device->openGroup == arg) { + device->openGroup = ""; // opening finished + } + if (!grp->shown) grp->showIt(true); + device->update(false); + } + + if (arg[0] != '-' || grp->shown) { + newline(); + sameRow = true; + device->x += 14; + add(item, grp->title); + if (shown && grp->arrow) { + yline = device->y + 2; + device->addChild(grp->arrow, indent + 2, yline); + grp->arrow->show(); + } + /* + if (device->doJump && !grp->shown) { + // open automatically group tree in visibleGr (clicked on graph icon) + snprintf(groupName, sizeof groupName, " %s ", grp->name()); + if (device->visibleGr.find(groupName) >= 0) { + grp->showIt(true); + grp->closeLater = true; + //printf("open %s\n", groupName); + } + } + */ + + if (rightItem) { +// device->addChild(rightItem->w, device->x + device->xspace, device->y); + rightItem = 0; + } else { + newline(); + } + } + tit=""; + ++sit; + grp->updateLayout(sit); + newline(); + if (grp->shown) { + grp->setLineWidth(1); + grp->setFrameStyle(QFrame::Box | QFrame::Sunken); + grp->setFixedSize(device->width(), device->y - yline); + device->y += 4; + device->nextY += 4; + } else { + grp->setFrameStyle(QFrame::MenuBarPanel); + grp->setFixedSize(device->width(), grp->title->height()); + } + grp->update(); + if (sit == device->code.end()) --sit; + break; + case 'V': // value for next item + value = arg; + break; + case 'i': // readonly + item = findItem(line); + rdonly = dynamic_cast(item->w); + if (rdonly == 0) { + rdonly = new RdOnly(device->viewport(), tit, value, arg); + initItem(item, rdonly, line); + } else { + rdonly->label->setText(tit); + } + if (tight) { + rdonly->label->setMinimumWidth(0); + rdonly->value->setMinimumWidth(0); + } else { + rdonly->label->setMinimumWidth(device->labelWidth); + rdonly->value->setMinimumWidth(device->leWidth); + } + add(item); + lwid = 0; + rdonly->setValue(value); + value=""; + break; + case 'I': // text input + item = findItem(line); + lineInp = dynamic_cast(item->w); + if (lineInp == 0) { + lineInp = new LineInput(device->viewport(), tit, arg); + initItem(item, lineInp, line, true); + } else { + lineInp->label->setText(tit); + } + lineInp->setLength(lwid); + if (tight) { + lineInp->label->setMinimumWidth(0); + } else { + lineInp->label->setMinimumWidth(device->labelWidth); + } + add(item); + lwid = 0; + lineInp->setValue(value); + value=""; + break; + case 'S': // set style + wStyle = arg[0]; + break; + case 'D': // divide column (typically -D2: double number of columns) + colDiv = atoi(arg); + break; + case 'L': // label + snprintf(labelCode, sizeof labelCode, "%d", labelNum); + labelNum++; + item = findItem(labelCode); + label = dynamic_cast(item->w); + if (label == 0) { + label = new QLabel(arg, device->viewport()); + initItem(item, label, labelCode); + } else { + label->setText(arg); + } + add(item); + break; + case 'M': // menu (experimental ?) + if (arg[0] == 0) { // end of menu + if (menu) { + for (i=menu->count(); iremoveItem(i); + } + } + menu=0; + value = ""; + break; + } + item = findItem(line); + menu = dynamic_cast(item->w); + if (menu == 0) { + menu = new Menu(tit, device->viewport(), arg); + initItem(item, menu, line, true); + } + menuItem = 0; + add(item); + break; + case 'm': // menu item + if (menu) { + if (menuItem < menu->count()) { + if (menu->text(menuItem).compare(value) != 0) { + menu->changeItem(arg, menuItem); + } + } else { + menu->insertItem(arg, menuItem); + } + menuItem++; + } + break; + case 'c': // pen style menu + item = findItem(line); + colorMenu = dynamic_cast(item->w); + if (colorMenu == 0) { + colorMenu = new ColorMenu(device->viewport(), arg); + initItem(item, colorMenu, line, true); + } + colorMenu->setValue(value); + colorMenu->setLength(lwid); + add(item); + case 'a': // autoclose on radio button (experimental?) + //device->closeGroup = this; + autocloseOnRadio = true; + //closeLater = true; + break; + case 'R': // radio button group + radioName = arg; + device->selectCmd = radioName; + device->selectCmd.append(" "); + device->selectCmd.append(value); + break; + case 'r': // radio button + if (radioName.isEmpty()) break; + radioLine = radioName + line; + line = radioLine.latin1(); + item = findItem(line); + if (tit == "") { + tit = arg; + } + rb = dynamic_cast(item->w); + if (rb == 0) { + rb = new RadioButton(tit, device->viewport(), radioName, arg); + if (isSelectMenu) { + initItem(item, rb, line, false); + connect(rb, SIGNAL(sendCmd(const char *)), device, SLOT(setSelectCmd(const char *))); + connect(rb, SIGNAL(clearOthers(const QString &)), this, SIGNAL(clearRadioButtons(const QString &))); + connect(this, SIGNAL(clearRadioButtons(const QString &)), rb, SLOT(setValue(const QString &))); + } else { + initItem(item, rb, line, true); +// if (device->closeGroup) { +// connect(rb, SIGNAL(clicked()), device->closeGroup, SLOT(toggle())); +// } + if (autocloseOnRadio) { + connect(rb, SIGNAL(clicked()), device, SLOT(closeMarkedGroups())); + } + } + } else { + rb->setText(tit); + } + if (sameRow) { + rb->setMinimumWidth(0); + } else { + rb->setMinimumWidth(device->labelWidth / 2); + } + add(item); + rb->setChecked(value.compare(arg) == 0); + tit=""; + break; + case 'B': // push button + item = findItem(line); + pb = dynamic_cast(item->w); + if (pb == 0) { + pb = new ClickButton(device->viewport(), arg); + pb->setLabel(tit); + switch (arg[0]) { + case 'C': + initItem(item, pb, line, false); + connect(pb, SIGNAL(clicked()), device, SLOT(switchLayout())); + break; + case 'D': + initItem(item, pb, line, false); + connect(pb, SIGNAL(clicked()), device, SLOT(selectIt())); + pb->setDefault(true); + break; + case 'S': + initItem(item, pb, line, false); + connect(pb, SIGNAL(sendCmd(const char *)), device, SLOT(openGrps(const char *))); + connect(pb, SIGNAL(changed()), device, SLOT(update())); + connect(pb, SIGNAL(clicked()), pb, SLOT(handleReturn())); + break; + default: + initItem(item, pb, line, true); + connect(pb, SIGNAL(clicked()), pb, SLOT(handleReturn())); + break; + } + } + add(item); + pb->setLabel(tit); + tit=""; + break; + case 'C': // check box + item = findItem(line); + cb = dynamic_cast(item->w); + if (cb == 0) { + cb = new CheckBox(tit, device->viewport(), arg); + initItem(item, cb, line, true); + if (autocloseOnRadio) { + connect(cb, SIGNAL(clicked()), device, SLOT(closeMarkedGroups())); + } + } else { + cb->setText(tit); + } + add(item); + cb->setValue(value); + value=""; + tit=""; + break; + /* + case 'X': // make graphic button + if (item) { + if (item->graphButton == 0) { + item->graphButton = new GraphButton(this, arg); + connect(item->graphButton, SIGNAL(selectItem(const char *)), + device, SLOT(selectGraphItem(const char *))); + } + if (device->graphOn) { + device->addChild(item->graphButton, device->x, device->y); + device->x += device->graphButtonWidth; + item->graphButton->show(); + arg = strrchr(arg, ' '); + if (arg) { + tip = "go to graph settings for this item ("; + tip.append(arg + 3); + tip.append(")"); + QToolTip::add(item->graphButton, tip); + tip = ""; + } + } else { + item->graphButton->hide(); + } + } + break; + */ + case 'g': // graph descriptor + if (device->graphDesc == "") { + device->graphDesc = arg; + } else if (device->graphDesc != arg) { + device->graphDesc = arg; + device->rebuildTime = DoubleTime() + 1; +// printf("*** desc changed\n"); + // a graph rebuild will become necessary + } else if (device->rebuildTime != 0 + && DoubleTime() > device->rebuildTime) { + // but we do not rebuild before the descriptor was two times the + // same in succession and at least 1 second has passed + device->rebuildTime = 0; + device->updateInterval = 0.5; +// printf("*** rebuild\n"); + QTimer::singleShot(1, device, SLOT(rebuild())); + } + break; + /* + case 'J': // jump to me + if (device->doJump) { + device->doJump = false; + device->yJump = device->y; + } + break; + */ + case 'E': // end of nested group + goto END; + default: + if (printit()) printf("%s\n", line); + } + } +END: +// if (device->closeGroup == this) { +// device->closeGroup = 0; +// } + /* graph obsolete + if (device->closeGr && shown && closeLater) { + // check if group is to be closed + snprintf(groupName, sizeof groupName, " %s ", name()); + if (device->visibleGr.find(groupName) < 0) { + closeLater = false; + showIt(false); + for (QDictIterator it(items); (item = it.current()); ++it) { + item->used = false; + } + } + } + */ + for (QDictIterator it(items); (item = it.current()); ++it) { + if (!item->used) { + grp = dynamic_cast(item->w); + if (grp) { + grp->hideContents(); + grp->arrow->hide(); + } + item->w->hide(); + /* + if (item->graphButton) { + item->graphButton->hide(); + } + */ + } + } +} + +void Group::appendTo(QString &c) { + Group *g; + Item *item; + + c.append(" "); + c.append(name()); + for (QDictIterator it(items); (item = it.current()); ++it) { + g = dynamic_cast(item->w); + if (g) { + if (g->shown) { + g->appendTo(c); + } + } + } +} + +void Group::autocloseAll() { + Group *g; + Item *item; + + for (QDictIterator it(items); (item = it.current()); ++it) { + g = dynamic_cast(item->w); + if (g) { + g->autocloseAll(); + if (g->shown && g->autoclose) { + printf("autoclose %s\n", g->name()); + g->toggle(); + } + } + } +} + +bool Group::openGroup(QString &path) { + Group *g; + Item *item; + + for (QDictIterator it(items); (item = it.current()); ++it) { + g = dynamic_cast(item->w); + if (g && PathStartsWith(path, g->name())) { + if (!g->shown) { + g->toggle(); + // g->autoclose = true; + } + if (path == g->name()) { + return g->shown; + } + return g->openGroup(path); + } + } + return false; +} + +Group* Group::findGroup(QString &path) { + Group *g; + Item *item; + + for (QDictIterator it(items); (item = it.current()); ++it) { + g = dynamic_cast(item->w); + if (g) { + if (PathStartsWith(path, g->name())) { + if (path == g->name()) return g; + return g->findGroup(path); + } + } + } + return NULL; +} + +Device::Device(QWidget* parent) + : QScrollView(parent) +{ + LineInput *lineInp; + + SetEraseColor(viewport(), topLevelWidget()); + setFrameStyle(0); + xspace=6; + yspace=4; + QString tit("This is a very long label"); + lineInp = new LineInput(this, tit, "test"); + lineInp->setLength(0); + columnWidth = lineInp->stdWidth() + xspace; + leWidth = lineInp->le->width(); + labelWidth = lineInp->label->width(); + // graphButtonWidth = 24; + lineHeight = QMAX(lineInp->le->sizeHint().height(), lineInp->label->sizeHint().height()); + delete lineInp; + /* + gButton = new GraphButton(this, "g-0"); + graphOn = false; + visibleGr = ""; + doJump = false; + yJump = 0; + */ + updateMode = update_idle; + stopUpdate = false; + //selectState = select_idle; + rebuildTime = 0; + //enableClipper(true); +} + +void Device::init(Command *command, SeaSet *initSet) { + + set = initSet; + com = command; + device = new Group(this, "main"); + select = new Group(this, "select", true); + mainGroup = device; + addChild(mainGroup, 0, 0); + y = yspace; + update(false); + /* + QToolTip::add(gButton, "graph settings"); + connect(gButton, SIGNAL(clicked()), this, SLOT(graphToggle())); + */ + + updateInterval = 5.0; + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timedUpdate())); + timer->start(10); +} + +void Device::rebuild() { + restart(graphDesc); +} + +/* +void Device::graphToggle() { + if (graphOn) { + QToolTip::add(gButton, "graph settings"); + columnWidth -= graphButtonWidth; + graphOn = false; + gButton->setPixmap(QPixmap(grPix)); + visibleGr = ""; + closeGr = true; + } else { + QToolTip::add(gButton, "close graph settings"); + columnWidth += graphButtonWidth; + graphOn = true; + gButton->setPixmap(QPixmap(gnPix)); + } + visibleGr = ""; + update(false); +} +*/ + +void Device::switchLayout() { + mainGroup->hideContents(); + if (mainGroup == select) { + mainGroup = device; + } else { + mainGroup = select; + } + update(false); +} + +void Device::selectIt() { + mainGroup->hideContents(); + com->sendCmd(selectCmd.latin1()); + mainGroup = device; + update(false); + QTimer::singleShot(100,this,SLOT(update())); +} + +void Device::setSelectCmd(const char *cmd) { + selectCmd = cmd; +} + +void Device::resizeEvent(QResizeEvent *e) { + if (e->size().width() != e->oldSize().width()) { + if (e->size().width() > 10 || e->oldSize().width() > 10) { + if (updateInterval > 0) { + updateInterval = 0; + QTimer::singleShot(100,this,SLOT(update())); + } + } + } + QScrollView::resizeEvent(e); +} + +/* +void Device::selectGraphItem(const char *id) { + //printf("SELECT %s\n", id); + visibleGr = ""; + if (id) { + visibleGr = " "; + visibleGr.append(id); + closeGr = true; + doJump = true; + } + update(false); +} +*/ + +void Device::timedUpdate() { + double now; + + if (updateMode == update_start) { +// printf("*** update start\n"); + updateStart(); + updateMode = update_complete; + tmot.start(); + } + if (updateMode == update_complete) { + if (tmot.elapsed() > 10000) { + set->sc->reconnect(); + tmot.start(); + printf("timeout -> reconnect\n"); + updateMode = update_idle; + } + if (set->sc->handleBuffer(0) <= 0) return; +// printf("*** update complete\n"); + if (set->sc->getResponse() < 0) { + set->sc->reconnect(); + printf("timeout -> reconnected\n"); + updateMode = update_idle; + return; + } + updateComplete(); + updateMode = update_idle; + return; + } + if (mainGroup == select && lastUpdate > 0) { + return; + } + now = DoubleTime(); + if (updateInterval == 0) { + updateInterval = 0.5; +// printf("*** immediate update\n"); + } else { + if (now < lastUpdate + updateInterval) { + return; + } + if (updateInterval < 0.99) { + updateInterval += 0.1; + } else { + updateInterval *= 2; + } + if (updateInterval >= 10) { + updateInterval = 10; + } +// printf("*** next update in %g sec\n", updateInterval); + } + if (isHidden() || height() < 3) { + lastUpdate = now; + return; + } + updateMode = update_start; +} + +void Device::activate() { + double now; + + if (mainGroup == select) return; + now = DoubleTime(); + if (now > lastUpdate) { + update(false); +// lastUpdate = now; + } +} + +void Device::closeMarkedGroups() { + mainGroup->autocloseAll(); + //selectState = select_idle; +} + +void Device::openGrps(const char *groups) { + Group *g; + bool openit; + + openGroup = groups + 1; // skip first letter S in button name + g = mainGroup->findGroup(openGroup); + openit = !g || !g->shown; + mainGroup->autocloseAll(); + if (openit) { + if (!mainGroup->openGroup(openGroup)) { + update(false); + return; + } + } + openGroup = ""; + update(false); +} + +void Device::update(bool refresh) { + if (refresh) { + updateInterval = 0.5; + stopUpdate = true; + } + lastUpdate = 0; +} + +void Device::updateStart() { + QString c("layout"); + + lastUpdate = DoubleTime(); + code.clear(); + mainGroup->appendTo(c); + /* + if (graphOn && !visibleGr.isEmpty()) { + if (doJump) { + c.append(visibleGr); + } else { + int i = visibleGr.findRev(' '); + if (i >= 0) { + c.append(visibleGr.mid(i)); + } + } + } + */ +// printf("*** SEND %s\n", c.latin1()); + set->sc->sendCommand(c.latin1()); + //printf("%s\n", c.latin1()); +} + +void Device::updateComplete() { + QString line; + + while (set->sc->getLine(line) > 0) { + code.append(line); + } + + if (stopUpdate) { + stopUpdate = false; + return; + } + + QStringList::Iterator it = code.begin(); + y = yspace; + nextY = yspace; + maxWid = 0; + +/* graph obsolete: + addChild(gButton, this->visibleWidth() - graphButtonWidth, 2); + gButton->show(); +*/ + // closeGroup = 0; + mainGroup->updateLayout(it); + resizeContents(maxWid, y); + +/* + if (yJump > 0) { + ensureVisible(0, yJump); + yJump = 0; + } +*/ +} diff --git a/device.h b/device.h new file mode 100644 index 0000000..dcb12e1 --- /dev/null +++ b/device.h @@ -0,0 +1,267 @@ +#include +#include +#ifndef DEVICE_H +#define DEVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "seaset.h" +#include "command.h" + +class Device; +class Group; + +class LineInput : public QHBox { + Q_OBJECT +public: + LineInput(QWidget *parent, QString &labelText, const char *var); + void setLength(int l); + int stdWidth(); + void setEraseColor(const QColor & color); +signals: + void sendCmd(const char *cmd); + void changed(); +public slots: + void handleReturn(); + void focusLost(); + void setValue(const QString &value); +public: + QLineEdit *le; + QLabel *label; +// bool graphOn; +private: + QString value; +}; + +class RdOnly : public QHBox { + Q_OBJECT +public: + RdOnly(QWidget *parent, QString &labelText, QString &val, const char *var); + void setLength(int l); +public slots: + void setValue(const QString &val); +public: + QLabel *label, *value; +private: + unsigned int len; +}; + +class CheckBox : public QCheckBox { + Q_OBJECT +public: + CheckBox(const QString &label, QWidget *parent, const char *var); +signals: + void sendCmd(const char *cmd); + void changed(); +public slots: + void handleReturn(); + void setValue(const QString &value); +}; + +class ClickButton : public QPushButton { + Q_OBJECT +public: + ClickButton(QWidget *parent, const char *var); + void setLabel(const QString &label); +signals: + void sendCmd(const char *cmd); + void changed(); +public slots: + void handleReturn(); +private: + int dx; +}; + +class RadioButton : public QRadioButton { + Q_OBJECT +public: + RadioButton(const QString &label, QWidget *parent, const QString &cmd, const char *value); +signals: + void sendCmd(const char *cmd); + void clearOthers(const QString &cmd); + void changed(); +public slots: + void handleClick(); + void setValue(const QString &value); +private: + QString cmd; +}; + +class Menu : public QComboBox { + Q_OBJECT +public: + Menu(const QString &label, QWidget *parent, const char *var); +signals: + void sendCmd(const char *cmd); + void changed(); +public slots: + void handleAct(const QString &value); + void setValue(const QString &value); +}; + +class ColorMenu : public QComboBox { + Q_OBJECT +public: + ColorMenu(QWidget *parent, const char *var); + ColorMenu(QString &color, QWidget *parent); + int getColorIndex(); + void setLength(int min, int max=0); +signals: + void sendCmd(const char *cmd); + void changed(); +public slots: + void handleAct(int index); + void handleChange(const QString &value); + void setValue(const QString &value); +private: + QString value; + void init(); + bool nochange; +}; + +class Item { +public: + QWidget *w; + bool used; + char wStyle; + QFont normalFont; + Group *grp; +// QPushButton *graphButton; + Item() { + this->used = true; + this->w = 0; + this->wStyle = 0; +// this->graphButton = 0; + } +}; + +/* +class GraphButton : public QPushButton { + Q_OBJECT +public: + GraphButton(QWidget *parent, const char *name); +signals: + void selectItem(const char *id); + void changed(); +public slots: + void handleClick(); +}; +*/ + +class Group : public QVBox { + Q_OBJECT +public: + Group(Device *p, const char *name, bool selectMenu=false); + Group(Group *p, const QString &tit, const char *name); + void updateLayout(QStringList::Iterator &it); + void appendTo(QString &c); + void hideContents(); + void autocloseAll(); + bool openGroup(QString &path); + Group* findGroup(QString &path); + bool shown; + bool autoclose; + +signals: + void changeHeight(); + void clearRadioButtons(const QString &except); + +public slots: + void toggle(); + +private: + void init(); + void add(Item *item, QWidget *w = 0); + void initItem(Item *item, QWidget *w, const char *line, bool active = false); + Item *findItem(const char *name); + void showIt(bool show); + void newline(); + + QPushButton *arrow; + QLabel *title; + Device *device; + Group *parentGroup; + QDict items; + int indent; // x indentation + bool sameRow; + bool tight; + QString tip; + char wStyle; + bool isSelectMenu; + int colDiv; + //bool closeLater; +}; + +typedef enum {select_idle, select_open} SelectState; + +class Device : public QScrollView +{ + Q_OBJECT + +friend class Group; + +public: + Device(QWidget* parent); + void init(Command *command, SeaSet *initSet); + +signals: + void restart(const char *vars); + +public slots: + void update(bool refresh = true); + void timedUpdate(); + void switchLayout(); + void selectIt(); + void setSelectCmd(const char *cmd); + // void graphToggle(); + // void selectGraphItem(const char *id); + void rebuild(); + void activate(); + void closeMarkedGroups(); + void openGrps(const char *groups); + +private: + void resizeEvent(QResizeEvent *e); + void updateStart(); + void updateComplete(); + + QString selectCmd; + SeaSet *set; + Group *mainGroup; + Group *device; + Group *select; + Command *com; + // GraphButton *gButton; + QStringList code; + int x, y; + int nextY; + int maxWid; + int xspace, yspace; + int labelWidth; + int leWidth; + int columnWidth; + // int graphButtonWidth; + int lineHeight; + QString graphDesc; + // bool graphOn; + // QString visibleGr; + // bool doJump; + QString openGroup; /* group path to open */ + SelectState selectState; + //int yJump; + bool stopUpdate; + double updateInterval; + double lastUpdate; + double rebuildTime; + // Group *closeGroup; + enum {update_idle, update_start, update_complete} updateMode; + QTime tmot; +}; + +#endif diff --git a/export.cpp b/export.cpp new file mode 100644 index 0000000..866de87 --- /dev/null +++ b/export.cpp @@ -0,0 +1,373 @@ +#include "export.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "seaset.h" +#include "sicsconn.h" +#include +#include + +void Export::addRow(QString label, QWidget *widget, QString tip) { + QLabel *labelW; + if (label != "") { + labelW = new QLabel(label, this); + lay->addWidget(labelW, row, 0); + if (tip != "") { + QToolTip::add(labelW, tip); + } + } + lay->addWidget(widget, row, 1); + if (tip != "") { + QToolTip::add(widget, tip); + } + row++; +} + +Export::Export(SeaSet *setArg, QWidget *parent, const char *instr) + : QWidget(parent) +{ + QPushButton *ok, *cancel; + char wd[PATH_MAX]; + + set = setArg; + row = 0; + instrument = instr; + + lay = new QGridLayout(this, 1, 3, 3, -1, "exportLayout"); + lay->setColStretch(2,1); + + addRow("export: ", new QLabel("all active curves", this), ""); + + lay->addWidget(new QLabel("step [sec]", this), 1, 0); + stepLE = new QLineEdit(this); + QValidator* validator = new QIntValidator( 1, 24*3600, this ); + stepLE->setValidator(validator); + stepLE->setText("auto"); + stepLE->setAlignment(Qt::AlignRight); + stepLE->setFixedWidth(100); + addRow("step [sec]", stepLE, + "time step [sec] (auto: 5 sec or more, max. 1000 points)" + ); + + scaleLE = new QLineEdit(this); + scaleLE->setText("1"); + scaleLE->setAlignment(Qt::AlignRight); + scaleLE->setFixedWidth(100); + addRow("x-scale [sec]", scaleLE, "enter 60 for minutes / 3600 for hours"); + + noneLE = new QLineEdit(this); + noneLE->setText("NaN"); + noneLE->setFixedWidth(100); + addRow("none value", noneLE, "value to be used for undefined numbers"); + + filePath = getcwd(wd, sizeof wd); + filePath.append("/seaexport.dat"); + + cancel = new QPushButton("Quit", this); + lay->addWidget(cancel, row, 0); + connect(cancel, SIGNAL(clicked()), this, SIGNAL(finish())); + + ok = new QPushButton("Export", this); + lay->addWidget(ok, 5, 1); + ok->setDefault(true); + ok->setFixedWidth(100); + addRow("", ok, ""); + connect(ok, SIGNAL(clicked()), this, SLOT(exportXY())); + + message = new QLabel("", this); + lay->addWidget(message, row, 1); + row++; + + lay->setRowStretch(row,1); +} + +void Export::showMe() { + show(); + updateCurveList(); +} + +void Export::updateCurveList() { + QString text("The following curves are active and to be exported:"); + int cnt=0; + SeaData *d; + + if (isHidden()) return; + for (d = set->dataList.first(); d != 0; d = set->dataList.next()) { + if (d->isActive()) { + text.append("\n "); + text.append(d->label); + cnt++; + } + } + if (cnt == 0) { + text = "no active curves"; + } + text.append("\n\ncurves are de-/activated by clicking on their legend"); + message->setText(text); + show(); +} + +void Export::progress(int bytes) { + QString text; + + if (progressBytes < 0) return; + if (isHidden()) return; + progressBytes += bytes; + if (progressBytes >= 0 && lastProgress.elapsed() > 250) { + lastProgress.start(); + text.sprintf("%.2f MBytes read", progressBytes * 0.000001); + message->setText(text); + show(); + procEvt(); + } +} + +void Export::exportXY() { + + QString cmd; + QString line; + int iret, cnt, icol; + bool ok; + time_t dt, t, from, to, step, lastRead, basis; + time_t i, lasti, len, siz, scale, mag, endi; + int prec; + SeaData *d; + QString text; + double val; + double *data; + double yy, yy0; + FILE *fil; + QString fileResult; + double eps; + static double undefpluseps = 0; + + fileResult = QFileDialog::getSaveFileName( + filePath, QString::null, this, + "save file dialog", + "select file to save exported data" + ); + if (fileResult == "") { + return; + } + filePath = fileResult; + + from = set->startRange; + to = set->endRange; + step = stepLE->text().toInt(); + if (step == 0) { /* auto */ + step = ((to - from) / 5000 + 1) * 5; + } + from = (from / step) * step; + scale = scaleLE->text().toInt(); + if (scale < 1) { + scale = 1; + } + len = (to - from - 1) / step; + to = from + len * step; + + if (step == 1) { + cmd.sprintf("graph %ld %ld text", from, to); + } else { + cmd.sprintf("graph %ld %ld np %ld", from, to, to - from + 2); + } + + cnt = 0; + for (d = set->dataList.first(); d != 0; d = set->dataList.next()) { + if (d->isActive()) { + cmd.append(" "); + cmd.append(d->name); + cnt++; + } + } + if (cnt == 0) { + message->setText("no curves active"); + show(); + return; + } + siz = len * cnt; + data = (double *)malloc(siz * sizeof(*data)); + if (data == NULL) { + message->setText("no memory"); + show(); + return; + } + + progressBytes = 0; + message->setText("waiting for server"); + procEvt(); + lastProgress.start(); + iret = set->ec->command(cmd); + progressBytes = -1; + + if (iret < 0) { + message->setText("can not connect to the GraphServer"); + goto err2; + } +//printf("%s\n", cmd.latin1()); + + text = "Curves:"; + message->setText(text); + procEvt(); + + iret = set->ec->getLine(line); + if (iret <= 0) { + message->setText("can not get response 1"); + goto err2; + } +//printf("> %s\n", line.latin1()); + + /* get returned absolute time */ + lastRead = line.toLong(); + //sscanf(line, "%ld", &lastRead); + + iret = set->ec->getLine(line); + if (iret <= 0) { + message->setText("can not get response 2"); + goto err2; + } + //printf("> %s\n", line.latin1()); + if (!line.startsWith("*")) { + message->setText("missing *"); + goto err2; + } + + icol = 0; + fil = fopen(filePath.latin1(), "w"); + fprintf(fil, "# SEA export from %s\n", instrument); + fprintf(fil, "# col 1: Time/%d sec since %s 0:00\n", (int)scale, set->dateLabel(0).latin1()); + + endi = 0; + for (d = set->dataList.first(); d != 0; set->dataList.findRef(d), d = set->dataList.next()) { + if (d->isActive()) { + yy0 = DATA_UNDEF; + yy = DATA_UNDEF; + lasti = -1; + //printf("- %s\n", d->label.latin1()); + fprintf(fil, "# col %d: %s (%s) %s\n", + icol + 2, d->name.latin1(), d->label.latin1(), + d->row->tagLabel->text().latin1()); + text.append("\n "); + text.append(d->label); + line = line.section(' ', 0, 0); + if (d->name == line.mid(1)) { + t = 0; + do { + iret = set->ec->getLine(line); + if (iret <= 0) { + message->setText("can not get line"); + goto err; + } +//printf("> %s\n", line.latin1()); + if (line.startsWith("*")) { + i = len; + } else { + /* decode line to x[n] / y[n] */ + dt = line.section(' ', 0, 0).toLong(&ok); + /* + iret = sscanf(line, "%ld %n", &dt, &pos); + if (iret < 1) { + */ + if (!ok) { + message->setText("bad timestep"); + goto err; + } + t += dt; + i = (t - from) / step; + yy0 = yy; + yy = line.section(' ', 1, 1).toDouble(&ok); + //iret = sscanf(line + pos, "%lf", &yy); + // printf("scan %8ld %9.4f\n", t - from, yy); + if (!ok) { // not a valid value + yy = DATA_UNDEF; + } else { + if (yy == DATA_UNDEF) { // by chance + if (undefpluseps == 0) { // create closest possible (but different) value to DATA_UNDEF + undefpluseps = yy; + eps = undefpluseps * 1e-9; + while (undefpluseps == DATA_UNDEF) { + undefpluseps += eps; + eps = eps * 2; + } + } + yy = undefpluseps; + } + } + } + /*---*/ + + if (lasti < i) { + if (lasti < 0) { + lasti = 0; + } + while (lasti < i - 1 && lasti < len) { + assert(lasti * cnt + icol < siz); + data[lasti*cnt + icol] = yy0; + lasti++; + } + if (lasti < len) { + data[lasti*cnt + icol] = yy; + // printf("%ld %9.4f\n", lasti, yy); + if (yy != DATA_UNDEF) { + if (lasti > endi && lasti != len) endi = lasti; + } + } + lasti++; + } + } while (!line.startsWith("*")); + } else { + for (i = 0; isetText(text); + procEvt(); + } + } + //fprintf(fil, "\n"); + basis = (from - set->base) / step; + //printf("endi %ld\n", endi); + for (i = 0; i < endi; i++) { + if (scale == 1) { + fprintf(fil, "%ld", (basis + i) * step); + } else { + mag = 10 * scale / step; + for (prec = 0; mag > 1; prec++,mag/=10); + fprintf(fil, "%.*f", prec, (double)(basis + i) * step / scale); + } + for (icol = 0; icol < cnt; icol++) { + val = data[i*cnt + icol]; + if (val == DATA_UNDEF) { + fprintf(fil, "\t%s", noneLE->text().latin1()); + } else { + // fprintf(fil, "\t%.7g", val); + // we might write here full precision, this will not watse space in case step is not 1 + fprintf(fil, "\t%.14g", val); + } + } + fprintf(fil,"\n"); + } + text.append("\n\nwritten to\n"); + text.append(filePath.latin1()); + message->setText(text); +err: + fclose(fil); +err2: + free(data); + show(); +} diff --git a/export.h b/export.h new file mode 100644 index 0000000..5948080 --- /dev/null +++ b/export.h @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "seaset.h" + +class Export : public QWidget +{ + Q_OBJECT + +public: + Export(SeaSet *setArg, QWidget *parent=0, const char *instr=0); + void showMe(); +private: + QLineEdit *stepLE; + QLineEdit *noneLE; + QLineEdit *scaleLE; + QLabel *message; + SeaSet *set; + QGridLayout *lay; + int row; + QString filePath; + const char *instrument; + int progressBytes; + QTime lastProgress; +signals: + void finish(); + void procEvt(); +public slots: + void updateCurveList(); + void exportXY(); + void addRow(QString label, QWidget *widget, QString tip); + void progress(int bytes); +}; diff --git a/graph.cpp b/graph.cpp new file mode 100644 index 0000000..0a86c36 --- /dev/null +++ b/graph.cpp @@ -0,0 +1,355 @@ +#include "graph.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "seaset.h" +#include "utils.h" + +static char *xZoomPix[] = { + "16 16 2 1", + " c None", + "X c #000000", + " ", + " ", + " X X ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " XX XX ", + " X X ", + " ", + " " +}; + +static char *leftPix[] = { + "16 16 2 1", + " c None", + "X c #000000", + " ", + " ", + " X ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " X ", + " ", + " " +}; + +static char *rightPix[] = { + "16 16 2 1", + " c None", + "X c #000000", + " ", + " ", + " X ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " XX ", + " X ", + " ", + " " +}; + +static char *undoPix[] = { + "16 16 2 1", + " c None", + "X c #000000", + " ", + " XXXXXXXXX ", + " XX ", + " XX ", + " X ", + " X ", + " X ", + " X X ", + " XX XX ", + " XX XX ", + " XXXXXXXXXXXX ", + " XX ", + " XX ", + " X ", + " ", + " " +}; + +Graph::Graph(const char* arg, QWidget *parent, long range) + : QWidget(parent, 0) +{ + QPixmap pmOut((const char **)xZoomPix); + QPixmap pmLeft((const char **)leftPix); + QPixmap pmRight((const char **)rightPix); + QPixmap pmUndo((const char **)undoPix); + QString str; + //QHBox *buttonBar; + QPushButton* buttonOk; + QPushButton* buttonAll; + QPushButton* buttonUndo; + QPushButton* buttonLeft; + QPushButton* buttonOut; + QPushButton* buttonRight; + QPushButton* buttonSettings; + QPushButton* buttonXY; + QSplitter *graphSplit; + QVBoxLayout* graphLayout; + QHBoxLayout* buttonLayout; + int w,h; + const char * vars; + + setName("Graph"); + + graphLayout = new QVBoxLayout(this, 0, 0, "graphLayout"); + + graphSplit = new QSplitter(Qt::Vertical, this, "splitter"); + graphLayout->add(graphSplit); + + buttonLayout = new QHBoxLayout(0, 6, 2, "buttonBar"); + graphLayout->addLayout(buttonLayout); + + buttonLayout->addWidget(new QLabel("Range", this)); + + lineEditRange = new QComboBox(true, this); + //SetLength(lineEditRange, 5, 6); + buttonLayout->addWidget(lineEditRange); + QToolTip::add(lineEditRange, "enter a number and m,h or d"); + + buttonOk = new QPushButton(this); + buttonOk->setDefault(true); + buttonOk->setText("OK"); + ShrinkButton(buttonOk); + buttonLayout->addWidget(buttonOk); + + buttonSettings = new QPushButton(this); + buttonSettings->setText("Settings"); + ShrinkButton(buttonSettings); + buttonLayout->addWidget(buttonSettings); + QToolTip::add(buttonSettings, "edit shown curves"); + + buttonAll = new QPushButton(this); + buttonAll->setText("Show All"); + ShrinkButton(buttonAll); + buttonLayout->addWidget(buttonAll); + QToolTip::add(buttonAll, "show all curves"); + + buttonXY = new QPushButton(this); + buttonXY->setText("export"); + ShrinkButton(buttonXY); + buttonLayout->addWidget(buttonXY); + QToolTip::add(buttonXY, "export data"); + + liveBox = new QCheckBox(this); + liveBox->setText("live mode"); + buttonLayout->addWidget(liveBox); + liveBox->setChecked(false); + + buttonLayout->addStretch(1); + + buttonUndo = new QPushButton(this); + buttonUndo->setPixmap(pmUndo); + w = QMAX(22, buttonUndo->sizeHint().width() - 6); + h = QMAX(22, buttonUndo->sizeHint().height() - 6); + if (w > h) { // Mac like + h += 6; + w -= 6; + } + buttonUndo->setFixedSize(w, h); + buttonLayout->addWidget(buttonUndo); + buttonUndo->setEnabled(false); + QToolTip::add(buttonUndo, "undo zoom or shift action"); + + buttonLeft = new QPushButton(this); + buttonLeft->setPixmap(pmLeft); + buttonLeft->setFixedSize(w, h); + buttonLayout->addWidget(buttonLeft); + QToolTip::add(buttonLeft, "scroll left"); + + buttonRight = new QPushButton(this); + buttonRight->setPixmap(pmRight); + buttonRight->setFixedSize(w, h); + buttonLayout->addWidget(buttonRight); + QToolTip::add(buttonRight, "scroll right"); + + buttonOut = new QPushButton(this); + buttonOut->setPixmap(pmOut); + buttonOut->setFixedSize(w, h); + buttonLayout->addWidget(buttonOut); + QToolTip::add(buttonOut, "zoom out horizontally"); + + str="Sea "; + str.append(arg); + topLevelWidget()->setCaption(str); + + clearWState(WState_Polished); + + unit='m'; + str="30m"; + + set = new SeaSet(graphSplit, range, "seaset"); + + const char *items[]={ + "1m","2m","5m","10m","15m","30m" + ,"1h","2h","4h","8h","16h" + ,"1d","2d","4d","8d",0}; + lineEditRange->insertStrList(items); + lineEditRange->setSizeLimit(15); + lineEditRange->setMaxCount(15); + lineEditRange->setEditText(str); + lineEditRange->lineEdit()->selectAll(); + + connect(lineEditRange, SIGNAL(activated(int)), + this, SLOT(activateRange())); + connect(buttonOk, SIGNAL(clicked()), + this, SLOT(activateRange())); + connect(buttonAll, SIGNAL(clicked()), + set, SLOT(showAll())); + connect(buttonSettings, SIGNAL(clicked()), + this, SIGNAL(gotoSettings())); + connect(buttonXY, SIGNAL(clicked()), + this, SIGNAL(openExport())); + connect(buttonUndo, SIGNAL(clicked()), + set, SLOT(undoZoom())); + connect(buttonLeft, SIGNAL(clicked()), + set, SLOT(shiftLeft())); + connect(buttonOut, SIGNAL(clicked()), + set, SLOT(zoomOutX())); + connect(buttonRight, SIGNAL(clicked()), + set, SLOT(shiftRight())); + connect(liveBox, SIGNAL(toggled(bool)), + set, SLOT(setLive(bool))); + connect(set, SIGNAL(undoEnabled(bool)), + buttonUndo, SLOT(setEnabled(bool))); + connect(lineEditRange->lineEdit(), SIGNAL(textChanged(const QString &)), + this, SLOT(autoReturn(const QString &))); + + set->setHost(arg); + + if (printit()) { + printf("reading curve list,\n"); + } + if (range < 365*24*3600) { + vars = "now"; + } else { + vars = NULL; + } + set->autoCurves(vars); + if (printit()) { + printf("opening window.\n"); + } + //lineEditRange->selectAll(); + + + QTimer *timer = new QTimer(this, "repeat"); + connect(timer, SIGNAL(timeout()), this, SLOT(liveUpdate())); + timer->start(2500); +} + +void Graph::liveUpdate() { + if (isShown() && width() > 3) { + set->liveUpdate(); + } +} + +void Graph::showIt(bool yes) { + if (yes) { + set->liveUpdate(); + show(); + } else { + hide(); + } +} + +void Graph::activateRange() { + long fact; + QString t; + double rng; + unsigned int j; + + //setFocus(); + t = lineEditRange->currentText(); + for (j = 0; j < t.length(); j++) { + if (t[j] > '9') { + unit = t.latin1()[j]; + t.truncate(j); + break; + } + } + switch (unit) { + case 'D': unit='d'; + case 'd': fact = 24*3600; break; + case 'H': unit='h'; + case 'h': fact = 3600; break; + case 'M': unit='m'; + case 'm': fact = 60; break; + case 'S': unit='s'; + case 's': fact = 1; break; + default: fact = 60; unit = 'm'; + } + rng = t.toDouble() * fact; + //printf("%s %lf %ld\n", t.latin1(), rng, fact); + if (rng > SEA_MAX_RANGE) { + unit = 'd'; + t.sprintf("%d%c", (SEA_MAX_RANGE + 36000) / 24 / 3600, unit); + lineEditRange->setEditText(t); + rng = SEA_MAX_RANGE; + } else if (rng < 59.5) { + rng = 60; + if (unit == 's') { + lineEditRange->setEditText("60s"); + } else { + unit = 'm'; + lineEditRange->setEditText("1m"); + } + } else { + t.append(unit); + lineEditRange->setEditText(t); + } + set->rescaleRange(long(rng)); + lineEditRange->lineEdit()->selectAll(); +} + +void Graph::autoReturn(const QString &text) { + if (0 == text.find(QRegExp("\\d{1,4}[DdHhMmSs]"))) { + activateRange(); + } else if (0 == text.find(QRegExp("[Qq]"))) { + exit(0); + } +} diff --git a/graph.h b/graph.h new file mode 100644 index 0000000..d21994b --- /dev/null +++ b/graph.h @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "seaset.h" + +class QVBoxLayout; +class QHBoxLayout; +class QSpacerItem; +class QPushButton; +class QCheckBox; + +class Graph : public QWidget +{ + Q_OBJECT + +public: + Graph(const char* host = 0, QWidget *parent=0, long range=1800); + + SeaSet *set; + QComboBox* lineEditRange; + QCheckBox* liveBox; + +public slots: + void activateRange(); + void autoReturn(const QString &); + void showIt(bool show); + void liveUpdate(); +signals: + void gotoSettings(); + void openExport(); + +private: + char unit; + void init(); +}; + + + diff --git a/instr_hosts.cpp b/instr_hosts.cpp new file mode 120000 index 0000000..fc5a977 --- /dev/null +++ b/instr_hosts.cpp @@ -0,0 +1 @@ +../six/instr_hosts.c \ No newline at end of file diff --git a/instr_hosts.h b/instr_hosts.h new file mode 120000 index 0000000..af7b719 --- /dev/null +++ b/instr_hosts.h @@ -0,0 +1 @@ +../six/instr_hosts.h \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8a52b6d --- /dev/null +++ b/main.cpp @@ -0,0 +1,521 @@ +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "instr_hosts.h" + +QPoint firstPos; + +FILE *fil; +char preffile[PATH_MAX]=""; +int prefdirlen = 0; +char prefil[80]=""; +char homeinstr[64]=""; +const char *instr; +//char prefinstr[64]=""; +char frominstr[128]; + +char *strtoupper(const char *str) { + static char buf[128]; + int i; + for (i = 0; i < 127; i++) { + if (str[i] == 0) break; + buf[i] = toupper(str[i]); + } + buf[i] = 0; + return buf; +} + +const char *fullinstr(const char *host, const char *instr) { + static char buf[128]; + if (strncasecmp(host, instr, strlen(instr)) == 0) { + return host; + } + snprintf(buf, sizeof buf, "%s at %s", host, strtoupper(instr)); + return buf; +} + +SeaApp::SeaApp(int &argc, char **argv) : QApplication(argc, argv) { + char *arg; + char *style=0; + char instrList[1024]; + QString deviceList; + int i; + + int port; + char host[64]; + int query; + struct tm tm; + time_t startupTime, now, range; + QString tip; + SicsConnection *seaman; + char *remotehost; + char *remoteinstr; + char *hostname; + char warning[256]; + int x, y, w, h; + QFont afont = font(); + afont.setPointSize(12); + setFont(afont); + + time(&lastActive); + time(&lastTimer); + tab = NULL; + sleepTime = 3600; + sleeping = false; + frominstr[0] = 0; + + home = InstrHost("sea", "", homeinstr, sizeof homeinstr, host, sizeof host, &port); + if (port == 0) { + homeinstr[0]='\0'; + } + remoteinstr = InstrHostRemoteInstr(); + hostname = InstrHostName(); + remotehost = InstrHostRemoteName(); + instr = NULL; + query = -1; + range = 1800; + setprintit(0); + for (i=1; i=argc) { + QStringList keys=QStyleFactory::keys(); + printf("styles: %s\n", keys.join(", ").latin1()); + ciao(); + } + style = argv[i]; + } else if (strcasecmp(arg,"-h") == 0) { + /* ??? */ + home = 1; + } else if (strcasecmp(arg,"-p") == 0) { + i++; + /* ignore obsolete pid file option */ + } else if (strcasecmp(arg,"-c") == 0) { + i++; + /* ignore obsolete watch/restart option */ + } else if (strcasecmp(arg,"-t") == 0) { + /* set sleep time (1 h by default) */ + i++; + if (isetHost("seaman", "sea"); + seaman->setUser("seauser seaser\n"); + + seaman->command(seamancmd.latin1()); + if (!seaman->getLine(deviceList)) { + deviceList = ""; + } + QStringList devlist(QStringList::split(',', deviceList, true)); + + seaman->disconnect(); + + int row = 1, col=0, cols = 4; + + menu.setCaption("Sea"); +// grid.setMargin(5); + + QLabel *label = new QLabel("Select instrument", &menu); + QFont font = label->font(); + font.setPointSize(18); + label->setFont(font); + QFont tooltipfont = QToolTip::font(); + QToolTip::setFont(font); + grid.addMultiCellWidget(label, 0, 0, 0, cols-1); + + QStringList::Iterator dit = devlist.begin(); + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it, ++dit) { + if ((*it).isEmpty()) { + if (col >= 0) { + col = 0; + row++; + } + } else { + rb = new QRadioButton(*it, &menu); + rb->setFont(font); + if (!(*dit).isEmpty()) { + QToolTip::add(rb, *dit); + } + grid.addWidget(rb, row, col); + QObject::connect(rb, SIGNAL(clicked()), &menu, SLOT(accept())); + bg.insert(rb); + col++; + if (col >= cols) { + col = 0; + row++; + } + if ((*it).compare(instr) == 0) { + rb->setChecked(true); + } + } + } + if (col > 0) { + row++; + col = 0; + } + + if (query == 1) { + /* + QString cbtext("default instrument when started from "); + cbtext += remotehost; + defi = new QCheckBox(cbtext, &menu); + if (prefinstr[0]) defi->setChecked(true); + grid.addMultiCellWidget(defi, row, row, 0, cols-1); + row++; + */ + } + + QPushButton ok("OK", &menu); + ok.setDefault(true); + ok.setFont(font); + connect(&ok, SIGNAL(clicked()), &menu, SLOT(accept())); + grid.addWidget(&ok, row, 0); + + QPushButton cancel("Quit", &menu); + cancel.setFont(font); + connect(&cancel, SIGNAL(clicked()), &menu, SLOT(reject())); + grid.addWidget(&cancel, row, cols-1); + + bg.hide(); + + if (printit()) { + printf("Instrument selection\n"); + } + if (menu.exec() == QDialog::Rejected) ciao(); + rb = dynamic_cast(bg.selected()); + if (!rb) ciao(); + instr = strdup(rb->text()); + /* + if (defi) { + if (defi->isChecked()) { + snprintf(prefinstr, sizeof prefinstr, "%s", instr); + } else { + prefinstr[0] = 0; + } + } + */ + QToolTip::setFont(tooltipfont); + } + + + if (frominstr[0]) { + snprintf(warning, sizeof warning, "WARNING: connected to %s,\n%s\n", strtoupper(instr), frominstr); + printf("\n%s\n", warning); + } else { + warning[0] = 0; + } + time(&startupTime); + if (printit()) { + printf("Sea %s starting,\n", instr); + } + + // read preferences + // include the screen size into the name: this helps if clients are used + // remotely from different screen sizes + + // new pref. file: include remote host name + snprintf(preffile, sizeof preffile, "%s/.seapref/%s_%s_%dx%d", + getenv("HOME"), remotehost, instr, desktop()->width(), desktop()->height()); + prefdirlen = strstr(preffile, "seapref/") + 7 - preffile; + fil = fopen(preffile, "r"); + if (fil) { /* new style pref file */ + char header[128]; + fgets(header, sizeof header, fil); // skip header + if (4 != fscanf(fil, "%d %d %d %d", &x, &y, &w, &h)) { + x = -1; + } + fclose(fil); + prefil[0] = 0; + } else { + // old style pref. file + snprintf(prefil, sizeof prefil, ".sea_%dx%d_%s", + desktop()->width(), desktop()->height(), instr); + fil = fopen(prefil, "r"); + if (fil) { + if (4 != fscanf(fil, "%d %d %d %d", &x, &y, &w, &h)) { + x = -1; + } + fclose(fil); + } else { + x = -1; + } + } + + + + tab = new Tab(strdup(instr), 0, range, warning); + if (tab->graph == NULL) ciao(); + connect(tab->device, SIGNAL(restart(const char*)), tab->graph->set, SLOT(restart(const char *))); + + if (x >= 0) { + tab->initSize(w, h); + tab->move(x, y); + } else { + tab->initSize(1000, 800); + } + tab->show(); + /* + QBoxLayout *lay = dynamic_cast(tab->layout()); + + QLayoutIterator it = lay->iterator(); + QLayoutItem *child; + while ( (child = it.current()) != 0 ) { + if (child->widget()) { + printf("- widget %s\n", child->widget()->name()); + } else if (child->spacerItem()) { + printf("- spacer\n"); + } else if (child->layout()) { + printf("- layout\n"); + } else { + printf("- null\n"); + } + ++it; + } + lay->insertWidget(3,tab->split); + */ + firstPos = tab->pos(); + tab->move(firstPos); + time(&now); + if (range > 365*24*3600) { + tip.sprintf("initially off"); + } else if ((int)(now - startupTime) < 7) { + tip.sprintf("initially on\n%d sec for startup:\nconnection seems fast enough", (int)(now - startupTime)); + tab->graph->liveBox->setChecked(true); + } else { + tip.sprintf("initially off\n%d sec for startup:\nconnection seems slow", (int)(now - startupTime)); + } + QToolTip::add(tab->graph->liveBox, tip); + + connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit())); +// connect(this, SIGNAL(lastWindowClosed()), this, SLOT(ciao())); + + connect(tab->exportXY, SIGNAL(procEvt()), this, SLOT(procEvt())); + connect(tab->settings->set, SIGNAL(gotoTime(time_t, time_t, time_t, bool)), + tab->report, SLOT(gotoTime(time_t, time_t, time_t, bool))); + connect(tab->report, SIGNAL(setMarker(time_t)), + tab->settings->set, SLOT(setMarker(time_t))); + connect(tab->settings->set->ec, SIGNAL(progress(int)), tab->exportXY, SLOT(progress(int))); + return; +} + +void Exit(int code) { + exit(code); +} + +void SeaApp::ciao() { + Exit(0); +} + +void SeaApp::fail() { + Exit(257); +} + +void SeaApp::procEvt() { + processEvents(100); +} + +QRect savedWinGeo; + +bool moveOrResize = true; + +bool SeaApp::notify(QObject *receiver, QEvent *e) { + time_t now; + bool result; + + QEvent::Type t = e->type(); + switch (t) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + case QEvent::KeyPress: + lastActive = time(NULL); + if (tab) { + if (sleeping) { + tab->wake(); + sleeping = FALSE; + } + tab->graph->set->runningMarkerOff(true); + result = QApplication::notify(receiver, e); + tab->graph->set->runningMarkerOff(false); + return result; + } + break; + case QEvent::Timer: + time(&now); + if (now > lastActive + sleepTime) { + if (frominstr[0]) + exit(); + if (lastTimer < now - 120) { + lastActive = now + 120 - sleepTime; + } +// if (home != 1 && now > lastActive + 24*3600) { +// exit(); // exit when not on instrument computer after one day +// } + if (now > lastActive + 24*3600) { + exit(); // exit anyway when not active for one day + } + if (!sleeping && tab != NULL) { + tab->sleep("sleeping ... click to wake up"); + sleeping = TRUE; + } + } + if (moveOrResize && now != lastTimer && tab != NULL && + tab->settings->set->sc->state != disconnected) { + moveOrResize = false; + QRect winGeo(tab->pos(), tab->size()); + if (winGeo != savedWinGeo) { + savedWinGeo = winGeo; + fil = fopen(preffile, "w"); + if (fil == NULL) { + preffile[prefdirlen] = 0; + mkdir(preffile, 0755); + chmod(preffile, 0755); + preffile[prefdirlen] = '/'; + fil = fopen(preffile, "w"); + } + if (fil) { + fprintf(fil, "#seaclient preferences V1.1\n%d %d %d %d\n", + winGeo.x(), winGeo.y(), + winGeo.width(), winGeo.height()); + fclose(fil); + } + } + } + lastTimer = now; + break; + case QEvent::Move: + case QEvent::Resize: + moveOrResize = true; + break; + case QEvent::WindowDeactivate: +// printf("deact\n"); + break; + default: + break; + } + return QApplication::notify(receiver, e); +} + +int main(int argc, char ** argv) { + SeaApp sea(argc, argv); +// printf("exit %d\n", sea.exec()); +// return 0; + return sea.exec(); +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..f8ecb3f --- /dev/null +++ b/main.h @@ -0,0 +1,26 @@ +#include +#include +#include +#include "tab.h" + +class SeaApp : public QApplication { + Q_OBJECT + +public: + SeaApp(int &argc, char **argv); + + Tab *tab; + int home; + time_t sleepTime; + +public slots: + void ciao(); + void fail(); + void procEvt(); + +private: + bool notify(QObject *recevier, QEvent *e); + time_t lastActive; + time_t lastTimer; + bool sleeping; +}; diff --git a/report.cpp b/report.cpp new file mode 100644 index 0000000..d031855 --- /dev/null +++ b/report.cpp @@ -0,0 +1,182 @@ +#include "report.h" +#include +#include +#include +#include + +Report::Report(QWidget *parent, SicsConnection *conn) : QWidget(parent, "report") +{ + QVBoxLayout *vbox = new QVBoxLayout(this, 0, 0, "report_vbox"); + QHBoxLayout *hdr = new QHBoxLayout(0, 3, 3, "report_hdr"); + + this->conn = conn; + + editButton = new QPushButton("edit", this); + hdr->addWidget(editButton); + + varEdit = new QLineEdit("tt.main", this); + hdr->addWidget(varEdit); + + leaveButton = new QPushButton("close", this); + hdr->addWidget(leaveButton); + + vbox->addLayout(hdr); + + textedit = new MyTextEdit(this, "report_edit"); + textedit->setReadOnly(TRUE); + textedit->setSelectionAttributes(1, QColor(255,255,128), FALSE); + + vbox->addWidget(textedit, 1); + + timer = new QTimer(this, "reportTimer"); + + connect(leaveButton, SIGNAL(clicked()), this, SIGNAL(leave())); + connect(editButton, SIGNAL(clicked()), this, SLOT(editReport())); + connect(timer, SIGNAL(timeout()), this, SLOT(updatePos())); + +} + +void Report::gotoTime(time_t from, time_t at, time_t to, bool silent) { + char cmd[128]; + int iret; + QString line; + time_t lastRead, t, dt; + bool ok; + QString message; + QDateTime datim; + int l; + int p; + + if (isHidden()) { + if (silent) return; + openMe(); + } + if (variable != varEdit->text() || from != this->from || to != this->to) { + + this->to = to; + this->from = from; + times.clear(); + + variable = varEdit->text(); + snprintf(cmd, sizeof cmd, "graph %ld %ld text np %ld %s", + from, to, to - from, variable.latin1()); + + textedit->clear(); + conn->command(cmd); + + iret = conn->getLine(line); + if (iret <= 0) { + message = ("can not get response 1"); + goto err; + } + + /* get returned absolute time */ + lastRead = line.toLong(); + + iret = conn->getLine(line); + if (iret <= 0) { + message = "can not get response 2"; + goto err; + } + + if (!line.startsWith("*" + variable)) { + message = "response '" + line + "' must start with '*" + variable + "'"; + goto err; + } + + t = 0; + for (;;) { + iret = conn->getLine(line); + if (iret <= 0) { + message = "can not get line"; + goto err; + } + //printf("> %s\n", line.latin1()); + if (line.startsWith("*")) { + break; + } + dt = line.section(' ', 0, 0).toLong(&ok); + if (!ok) { + message = "bad timestep"; + goto err; + } + t += dt; + times.append(t); + datim.setTime_t(t); + textedit->appendHtml("" + datim.toString("hh:mm:ss ") + ""); + textedit->appendText( + line.section(' ', 1).replace("\\n","\n").replace("\\t","\t").replace("\\\\","\\") + +"\n" + ); + } + textedit->append("\n"); + pos = -1; + } + l = times.count(); + + p = l - 1; + for (int ipos = 0; ipos < l; ipos++) { + if (times[ipos] >= at) { + p = ipos - 1; + break; + } + } + if (p < 0) p = 0; + if (pos < 0) { + pos = l; + } + setCursorPos(p); + timer->start(200); + return; +err: + textedit->append("error: "+message); +} + +void Report::setCursorPos(int newpos) { + int index; + + textedit->setCursorPosition(newpos, 0); + if (newpos > pos) { + textedit->moveCursor(QTextEdit::MoveDown, false); + textedit->moveCursor(QTextEdit::MoveDown, false); + } else { + textedit->moveCursor(QTextEdit::MoveUp, false); + textedit->moveCursor(QTextEdit::MoveUp, false); + } + textedit->getCursorPosition(&posc, &index); + pos = newpos; + textedit->setSelection(pos, 0, pos+1, 0, 1); + textedit->setCursorPosition(posc, 0); + textedit->ensureCursorVisible(); +} + +void Report::updatePos() { + int para, index; + + textedit->getCursorPosition(¶, &index); + if (para == pos) return; + if (para == posc && index == 0) { + // cursor repositioning pending + posc = pos; + textedit->setCursorPosition(posc, 0); + textedit->ensureCursorVisible(); + } else { + setCursorPos(para); + } + if (pos < (int)times.count()) { + setMarker(times[pos]); + } +} + +void Report::editReport() { + QString text; + + if (textedit->isReadOnly()) { + textedit->setReadOnly(FALSE); + timer->stop(); + setMarker(0); + } else { + printf("%s\n", textedit->text().latin1()); + textedit->setText(textedit->text()); + } +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..f13d752 --- /dev/null +++ b/report.h @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include +#include "sicsconn.h" +#include "utils.h" + +class Report : public QWidget +{ + Q_OBJECT +public: + Report(QWidget *parent, SicsConnection *conn); +signals: + void leave(); + void openMe(); + void setMarker(time_t t); +public slots: + void gotoTime(time_t from, time_t at, time_t to, bool silent); + void updatePos(); + void editReport(); +private: + void setCursorPos(int newpos); + MyTextEdit *textedit; + QPushButton *editButton; + QPushButton *leaveButton; + QLineEdit *varEdit; + time_t atTime; + QString variable; + SicsConnection *conn; + int pos; + int posc; + time_t from, to; + QValueVector times; + QTimer *timer; +}; diff --git a/seaplot.cpp b/seaplot.cpp new file mode 100644 index 0000000..5056074 --- /dev/null +++ b/seaplot.cpp @@ -0,0 +1,722 @@ +#include "seaplot.h" +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +#define SEA_RESOLUTION 0.00005 /* chagned from 0.0005 for Amy, July 2020 */ + +LegendButton::LegendButton(QWidget *parent, SeaPlot *plot, SeaCurve *crv) : QwtLegendButton(parent) { + this->plot = plot; + this->crv = crv; + connect(this, SIGNAL(clicked()), this, SLOT(legendClicked())); + connect(&clickTimer, SIGNAL(timeout()), this, SLOT(handleClickTimer())); + label = new FlatButton(crv->title(), parent); + setFixedHeight(label->sizeHint().height()); + setFixedWidth(16); + connect(label, SIGNAL(clicked()), this, SLOT(legendClicked())); + connect(label, SIGNAL(mousePressed()), this, SLOT(mousePressed())); +}; + +void LegendButton::init(SeaPlot *plot, SeaCurve *crv) { + this->plot = plot; + this->crv = crv; +}; + +void LegendButton::mousePressed() { + clickDone = false; + clickTimer.stop(); + clickTimer.start(300); +} + +void LegendButton::handleClickTimer() { + if (!clickDone) { + clickTimer.stop(); + clickDone = true; + longClick(); + } +} + +void LegendButton::legendClicked() { + if (!clickDone) { + clickDone = true; + clickTimer.stop(); + plot->switchBold(crv); + } +}; + +void LegendButton::longClick() { + plot->setOneBold(crv); +}; + + +void LegendButton::mousePressEvent(QMouseEvent *e) { + mousePressed(); + QwtLegendButton::mousePressEvent(e); +} + +void LegendButton::setTitle(const QString &title) { + label->setText(title); +} + +class TimeScaleDraw: public QwtScaleDraw { +public: + TimeScaleDraw(SeaSet *set) : QwtScaleDraw() { + this->set = set; + } + virtual QString label(double v) const { + QTime time; + QwtScaleDiv div = scaleDiv(); + double step = div.majStep(); + int itic0 = lround(div.lBound() / step + 0.5001); + int itic1 = lround(div.hBound() / step - 0.5001); + int itic = lround(v / step);; + int m; + + if (step < 23*3600) { + m = 2; + if (step > 59 && step < 121) { + m = 5; + } else if (lround(step) == 600) { + m = 3; + } + } else { + m = (itic1 - itic0) / 5 + 1; + } + time.setHMS(0,0,0); + itic0 = lround(div.lBound() / step / m + 0.5001); + itic1 = lround(div.hBound() / step / m - 0.5001); + if (itic1 - itic0 > 1 && v > div.hBound() - step) { + return ""; //do not show last label + } else if (itic % m == 0) { + if (step < 3599) { + return time.addSecs(lround(v)).toString("hh:mm"); + } else if (lround(v/3600) % 24 == 0) { + return set->dateLabel(lround(v/(24*3600))); + } else { + return time.addSecs(lround(v)).toString("hh").append("h"); + } + } else { + return ""; + } + } + SeaSet *set; +}; + +class YScaleDraw: public QwtScaleDraw { +public: + YScaleDraw(SeaSet *set) : QwtScaleDraw() { + this->set = set; + } + virtual int maxLabelWidth(const QFontMetrics &fm) const { + return set->leftLabelWidth(QwtScaleDraw::maxLabelWidth(fm)); + } + SeaSet *set; +}; + +class SeaZoomer: public QwtPlotZoomer { +friend class SeaPlot; +public: + SeaZoomer(SeaPlot *p) : + QwtPlotZoomer(QwtPlot::xBottom, QwtPlot::yLeft, + QwtPicker::DragSelection, QwtPicker::AlwaysOff, + p->canvas()) { + yAuto = true; + displayMarker = false; + pl = p; + doZoom = false; + }; + SeaPlot *pl; + bool yAuto; + bool setMark; + bool displayMarker; + bool track; + bool doZoom; + int xp, yp; + QTime t; + +protected: + QwtDoubleSize minZoomSize() const { + const QwtScaleDiv *scy = pl->axisScale(QwtPlot::yLeft); + double hei = fabs(scy->hBound() + scy->lBound()) * SEA_RESOLUTION; + return QwtDoubleSize(60., hei); + }; + + void rescale() { + QwtDoubleRect r = zoomRect(); + if (doZoom && r != scaleRect()) { + pl->zoomTo(scaleRect(), r); + } + } + + void enableMarker() { + if (!displayMarker) { + QWidget *parent = parentWidget(); + track = parent->hasMouseTracking(); + parent->setMouseTracking(true); + displayMarker = true; + } + } + + void disableMarker() { + if (displayMarker) { + QWidget *parent = parentWidget(); + displayMarker = false; + parent->setMouseTracking(track); + } + } + + void widgetMousePressEvent(QMouseEvent *e) { + //printf("*** mousePress %d %d\n", e->x(), e->y()); + xp = e->x(); + yp = e->y(); + if (pl->set->markerFix) { + setMark = true; + } else { + setMark = !pl->set->markerWasOn; + } + pl->set->markerFix = false; + t.start(); + QwtPlotZoomer::widgetMousePressEvent(e); + } + + void widgetMouseReleaseEvent(QMouseEvent *e) { + //printf("*** mouseRelease %d %d %d\n", e->x(), e->y(), t.elapsed()); + // mechanism for avoiding unintended zooms: + // do not allow if the rectangle is too small + // or if the mouse was pressed for less than 200 ms + doZoom = abs(e->x() - xp) + abs(e->y() - yp) > 2 && t.elapsed() > 200; + QwtPlotZoomer::widgetMouseReleaseEvent(e); + if (!doZoom && !displayMarker && setMark) { + QwtDoublePoint p = invTransform(e->pos()); + pl->set->setMarker(p.x()); + } + } + + void widgetMouseDoubleClickEvent(QMouseEvent *e) { + //printf("*** mouseDoubleClick %d %d %d\n", e->x(), e->y(), t.elapsed()); + setMark = true; + pl->set->markerFix = true; + QwtDoublePoint at =invTransform(e->pos()); + //pl->set->gotoTime(at.x(), FALSE); + } + + void widgetMouseMoveEvent(QMouseEvent *e) { + if (displayMarker) { + QwtDoublePoint p = invTransform(e->pos()); + if (!pl->set->markerFix) { + pl->set->setMarker(p.x()); + pl->set->gotoTime(p.x(), TRUE); + } + } else { + QwtPlotZoomer::widgetMouseMoveEvent(e); + } + } +}; + +SeaCurve::SeaCurve(QwtPlot *parent, const QString &title) : QwtPlotCurve(parent, title) { + leg = NULL; + iColor = -1; +}; + +void SeaCurve::draw(QPainter *p, const QwtDiMap &xMap, const QwtDiMap &yMap, + int from, int to) { + int i, start; + double ylowlim; + + if (leg->plot->logarithmic) { + ylowlim = 1e-30; + } else { + ylowlim = DATA_UNDEF; + } + start = -1; + if (to < 0) { + to = dataSize() - 1; + if (to < 0) return; + } + for (i=from; i<=to; i++) { + if (i > 0 && y(i) <= ylowlim) { /* undefined value */ + if (start >= 0) { + QwtPlotCurve::draw(p, xMap, yMap, start, i-1); + start = -1; + } + } else { + if (start < 0) start = i; + } + } + if (start >= 0) { + QwtPlotCurve::draw(p, xMap, yMap, start, to); + } +}; + +void SeaCurve::setPen(const QPen &p) { + QwtCurve::setPen(p); + if (leg) leg->setCurvePen(p); +}; + +void SeaCurve::setTitle(const QString &t) { + QwtCurve::setTitle(t); + if (leg) leg->setTitle(t); +}; + +void SeaCurve::yBounds(double from, double to, double &ymin, double &ymax) { + int i, n = dataSize(); + double y1, y2; + double ya, yb, yc, yd, yi; + int k, n1, n2, nn; + bool done; + double ylowlim; + +// implementing a mechanism to ignore spikes: +// after the first pass, the whole range is divided into 3 ranges. +// in the next pass, only the points in the middle range are taken into account +// for minimum and maximum, the others are counted only. When the number of points +// in the upper or in the lower part are less than 2 %, the range is reduced by +// the upper and/or the lower part and a next pass is done + + if (leg->plot->logarithmic) { + ylowlim = 1e-30; + } else { + ylowlim = DATA_UNDEF; + } + y1 = 1e30; + y2 = -1e30; + ya = -1e30; + yb = -1e30; + yc = 1e30; + yd = 1e30; + k = 0; + yi = 1e30; + while (1) { + n1 = 0; + n2 = 0; + nn = 0; + for (i=0; i= from) { + break; + } + } + for (; i to) { + break; + } + yi = y(i); + if (yi > ylowlim) { // and therefore yi != DATA_UNDEF + if (yi < yb) { + n1++; + } else if (yi > yc) { + n2++; + } else { + nn++; + if (yi > y2) { + y2 = yi; + } + if (yi < y1) { + y1 = yi; + } + } + } + } + if (i == n && yi > ylowlim) { + if (yi < y1) y1 = yi; + if (yi > y2) y2 = yi; + } + if (k == 0) { + ya = y1; + yd = y2; + } else { + done = true; + if (n1 * 49 < nn + n2 && y1 >= yb) { + ya = y1; + done = false; + } + if (n2 * 49 < nn + n1 && y2 <= yc) { + yd = y2; + done = false; + } + if (done || k > 20 || ya == yd) { + break; + } + //printf("%s %d %d %d %d %f %f\n", data->label.latin1(), k, n1, n2, nn, ya, yd); + } + k++; + yb = ya + (yd - ya) / 3; + yc = yd - (yd - ya) / 3; + y1 = yc; + y2 = yb; + } + if (yi < 0.9e30) { + ymin = QMIN(ymin, ya); + ymax = QMAX(ymax, yd); + } + return; +} + +void SeaCurve::changeBold() { + int i; + if (iColor == -1) { + i = Convert2ColorIndex("black"); + } else { + i = iColor; + } + leg->plot->set->saveBoldState(data); + if (data->bold) { + setPen(thisPen(i, 0)); + QToolTip::add(leg, "hide curve (long click: show this curve only)"); + QToolTip::add(leg->label, "hide curve (long click: show this curve only)"); + } else { + setPen(thisPen(i, 1)); + QToolTip::add(leg, "show curve (long click: this curve only)"); + QToolTip::add(leg->label, "show curve (long click: this curve only)"); + } +} + +SeaPlot::SeaPlot(QWidget *p, SeaSet *set, const QString &name) : QwtPlot(p, name) { + QTime now(QTime::currentTime()); + QTime midnight(0,0,0); + double dnow; + + //setAutoLegend( TRUE ); + //setLegendPosition( QwtPlot::Left , 0.5); + this->set = set; + + dnow = midnight.secsTo(now); + setAxisScale(QwtPlot::xBottom, dnow-1800, dnow+600, 300.0); + setAxisScale(QwtPlot::yLeft, 0.0, 0.9999); + setAxisScaleDraw(QwtPlot::xBottom, new TimeScaleDraw(set)); + setAxisScaleDraw(QwtPlot::yLeft, new YScaleDraw(set)); + setAxisLabelFormat(QwtPlot::yLeft, 'g', 5); + zoom = new SeaZoomer(this); + zoom->setRubberBand(QwtPlotZoomer::RectRubberBand); + zoom->setRubberBandPen(QPen(Qt::red)); + plotLayout()->setCanvasMargin(0, SeaPlot::yLeft); + plotLayout()->setCanvasMargin(0, SeaPlot::yRight); + canvas()->setPaletteBackgroundColor(QColor(238,238,238)); + setGridPen(QPen(QColor(221, 221, 221), 1)); + logarithmic = false; + removeAll(); +}; + +void SeaPlot::removeAll() { + tag = ""; + removeCurves(); + marker = insertCurve("Marker"); +// setCurvePen(marker, QPen(Qt::white, 3)); + setCurvePen(marker, QPen(Qt::black, 1)); +} + +void SeaPlot::setMarker(double x) { + const QwtScaleDiv *s = axisScale(yLeft); + double xval[2], yval[2], h; + SeaCurve *crv; + QwtPlotCurveIterator itc = curveIterator(); + + if (x == DATA_UNDEF) { + zoom->disableMarker(); + xval[0] = DATA_UNDEF; + xval[1] = DATA_UNDEF; + } else { + zoom->enableMarker(); + xval[0] = x; + xval[1] = x; + } + h = s->hBound() - s->lBound(); + if (logarithmic) { + yval[0] = s->lBound() * 0.5; + } else { + yval[0] = s->lBound() - h; + } + yval[1] = s->hBound() + h; + setCurveData(marker, xval, yval, 2); + + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { + crv->data->showValueAt(x); + } + } +} + +void SeaPlot::refresh() { + const QwtScaleDiv *s = axisScale(xBottom); + replotRange(s->lBound(), s->hBound(), s->majStep()); +}; + +void SeaPlot::autoColors() { + SeaCurve *crv; + QwtPlotCurveIterator itc = curveIterator(); + int i; + + usedColors.fill(false, N_SEACOLORS); + usedColors.setBit(0); // do not use white + + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { + if (crv->iColor >= 0 && crv->iColor < N_SEACOLORS) { + usedColors.setBit(crv->iColor); + } + } + } + + i=0; + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { + if (crv->iColor < 0) { + while (i < N_SEACOLORS && usedColors.at(i)) { + i++; + } + if (i >= N_SEACOLORS) { + crv->iColor = Convert2ColorIndex("black"); + } else { + crv->iColor = i; + usedColors.setBit(i); + crv->changeBold(); + } + } + } + } +} + +LegendButton *SeaPlot::putCurve(SeaData *d, QWidget *parent, LegendButton *legButton) { + SeaCurve *crv = new SeaCurve(this, d->label); + + d->plot = this; + d->key = insertCurve(crv); + + if (legButton) { + legButton->init(this, crv); + } else { + legButton = new LegendButton(parent, this, crv); + } + legButton->setTitle(d->label); + + //QFont myfont(legButton->font()); + //myfont.setPointSize(12); + //legButton->setFont(myfont); + //legButton->setFixedHeight(legButton->fontMetrics().height()+2); + + crv->leg = legButton; + //crv->leg->setTitle(crv->title()); + //crv->leg->setTitle(""); + //if (d->style >= 0 && d->style <= N_SEACOLORS) { + if (d->style >= 0) { + crv->iColor = d->style; + crv->autoColor = false; + } else { + crv->iColor = -1; + crv->autoColor = true; + } + crv->data = d; + crv->changeBold(); + crv->setStyle(QwtCurve::Lines); + d->modified = true; + d->update(); + return legButton; +} + +void SeaPlot::showAgain() { + SeaCurve *crv; + QwtPlotCurveIterator itc = curveIterator(); + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { + crv->data->bold = true; + crv->changeBold(); + } + } + autoColors(); + //refresh(); + set->replotAnyway(); +} + +bool SeaPlot::toggleLinLog() { + logarithmic = ! logarithmic; + if (logarithmic) { + setAxisOptions(QwtPlot::yLeft, QwtAutoScale::Logarithmic); + } else { + setAxisOptions(QwtPlot::yLeft, QwtAutoScale::None); + } + refresh(); + return logarithmic; +} + +void SeaPlot::zoomMax() { + autoZoom(true); + refresh(); +} + +void SeaPlot::autoZoom(bool on) { + zoom->yAuto = on; + setAutoOff(!on); +} + +void SeaPlot::undoZoom(QwtDoubleRect &r) { + setAxisScale(QwtPlot::yLeft, r.y1(), r.y2()); + zoom->setZoomBase(); +} + +void SeaPlot::zoomTo(QwtDoubleRect old, QwtDoubleRect &r) { + set->saveRange(this, old, zoom->yAuto); + autoZoom(false); + setAxisScale(QwtPlot::yLeft, r.y1(), r.y2()); + set->rescale(r.x1(), r.x2()); + zoom->setZoomBase(); +} + +void SeaPlot::zoomOut() { + QwtDoubleRect r = zoom->zoomRect(); + QwtDoubleRect old= zoom->scaleRect(); + + set->saveRange(this, old, zoom->yAuto); + if (logarithmic) { + double h = log(r.y2()) - log(r.y1()); + r.setY2(exp(log(r.y2()) + h / 2)); + r.setY1(exp(log(r.y1()) - h / 2)); + } else { + double h = r.y2() - r.y1(); + r.setY2(r.y2() + h / 2); + r.setY1(r.y1() - h / 2); + } + zoom->setZoomBase(r); + zoom->zoom(0); + autoZoom(false); + refresh(); +} + +void SeaPlot::shiftUp() { + QwtDoubleRect r = zoom->zoomRect(); + double h = (r.y2() - r.y1()) * 0.5; + QwtDoubleRect old= zoom->scaleRect(); + + set->saveRange(this, old, zoom->yAuto); + r.setY2(r.y2() + h); + r.setY1(r.y1() + h); + zoom->setZoomBase(r); + zoom->zoom(r); + zoom->setZoomBase(); + zoom->zoom(0); + autoZoom(false); + refresh(); +} + +void SeaPlot::shiftDown() { + QwtDoubleRect r = zoom->zoomRect(); + double h = (r.y2() - r.y1()) * 0.5; + QwtDoubleRect old= zoom->scaleRect(); + + set->saveRange(this, old, zoom->yAuto); + r.setY2(r.y2() - h); + r.setY1(r.y1() - h); + zoom->setZoomBase(r); + zoom->zoom(r); + zoom->setZoomBase(); + zoom->zoom(0); + autoZoom(false); + refresh(); +} + +int SeaPlot::replotRange(double from, double to, double tick) { + SeaCurve *crv; + QwtDoubleRect r; + double ymin, ymax, yminA, ymaxA, y0, res; + int result; + bool nobold; + + result = 1; + ymin = 1e30; + ymax = -1e30; + yminA = ymin; + ymaxA = ymax; + nobold = true; + QwtPlotCurveIterator itc = curveIterator(); + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { + crv->yBounds(from, to, yminA, ymaxA); + if (crv->data->bold) { + crv->yBounds(from, to, ymin, ymax); + } + } + } + if (ymin > ymax) { + ymin = yminA; + ymax = ymaxA; + if (ymin > ymax) { + ymin = 0; + ymax = 1; + result = 0; + } + } + r = zoom->zoomRect(); + if (zoom->yAuto) { + res = 0.1; + } else { + ymin = r.y1(); + ymax = r.y2(); + res = SEA_RESOLUTION; + } + if (logarithmic) { + if (ymax <= 0) { + ymax = ymaxA; + ymin = yminA; + } else if (ymin <= 0) { + ymin = QMIN(ymax, yminA); + } + y0 = (log(ymin) + log(ymax)) / 2; + if ((log(ymax) - log(ymin)) < res) { + ymin = exp(y0 - res * 0.4999); + ymax = exp(y0 + res * 0.5); + } + } else { + y0 = (ymin + ymax) / 2; + if ((ymax - ymin) < y0 * res) { + ymin = y0 * (1 - res * 0.4999); + ymax = y0 * (1 + res * 0.5); + } + } + setAxisMaxMinor(QwtPlot::xBottom, 0); + setAxisScale(QwtPlot::xBottom, from, to, tick); + setAxisScale(QwtPlot::yLeft, ymin, ymax); + replot(); + if (zoom->zoomRectIndex() == 0) { + zoom->setZoomBase(); + } + return result; +}; + +int SeaPlot::replotRange() { + const QwtScaleDiv *scx = axisScale(QwtPlot::xBottom); + return replotRange(scx->lBound(), scx->hBound(), scx->majStep()); +}; + +void SeaPlot::switchBold(SeaCurve *crv) { + crv->data->bold = !crv->data->bold; + crv->changeBold(); + crv->leg->update(); + set->replotAnyway(); + set->updateCurveList(); +} + +void SeaPlot::setOneBold(SeaCurve *selectedCrv) { + QwtPlotCurveIterator itc = curveIterator(); + SeaCurve *crv; + + for (QwtPlotCurve *c = itc.toFirst(); c != 0; c = ++itc ) { + crv = dynamic_cast(c); + if (crv) { // omit Marker + crv->data->bold = crv == selectedCrv; + crv->changeBold(); + crv->leg->update(); + } + } + autoZoom(true); + set->replotAnyway(); + set->updateCurveList(); +} + diff --git a/seaplot.h b/seaplot.h new file mode 100644 index 0000000..6a9f4b0 --- /dev/null +++ b/seaplot.h @@ -0,0 +1,97 @@ +#ifndef SEAPLOT_H +#define SEAPLOT_H + +#include +#include +#include +#include +#include "seaset.h" + +class SeaSet; +class SeaData; +class SeaZoomer; +class SeaCurve; +class LegendButton; +class QGrid; +class QLineEdit; + +class SeaPlot : public QwtPlot +{ + Q_OBJECT + +public: + SeaPlot(QWidget *p, SeaSet *set, const QString &name); + void removeAll(); + LegendButton *putCurve(SeaData *d, QWidget *parent, LegendButton *legButton = NULL); + int replotRange(double from, double to, double tick); + int replotRange(); + void refresh(); + void switchBold(SeaCurve *crv); + void setOneBold(SeaCurve *crv); + void showAgain(); + SeaSet *set; + QString tag; + void autoZoom(bool on); + QwtDoubleRect undoRect; + void zoomTo(QwtDoubleRect old, QwtDoubleRect &r); + void undoZoom(QwtDoubleRect &r); + bool toggleLinLog(); + void autoColors(); + long marker; + void setMarker(double x); + bool logarithmic; + QBitArray usedColors; +signals: + void setAutoOff(bool off); +private slots: + void zoomOut(); + void zoomMax(); + void shiftUp(); + void shiftDown(); +private: + SeaZoomer *zoom; + double y1, y2; +}; + +class FlatButton; + +class LegendButton : public QwtLegendButton { + Q_OBJECT +public: + LegendButton(QWidget *parent, SeaPlot *plot, SeaCurve *crv); + void init(SeaPlot *plot, SeaCurve *crv); + void setTitle(const QString &title); + FlatButton *label; + QLabel *valueLabel; + SeaPlot *plot; + QTimer clickTimer; +protected: + void mousePressEvent(QMouseEvent *e); +public slots: + void legendClicked(); + void longClick(); + void handleClickTimer(); + void mousePressed(); +private: + SeaCurve *crv; + bool clickDone; +}; + + +class SeaCurve : public QwtPlotCurve { +public: + SeaCurve(QwtPlot *parent, const QString &title); + void draw(QPainter *p, const QwtDiMap &xMap, const QwtDiMap &yMap, int from, int to); + void setPen(const QPen &p); + void setTitle(const QString &t); + void yBounds(double from, double to, double &ymin, double &ymax); + void changeBold(); + + SeaData *data; + bool bold; + int iColor; + bool autoColor; + LegendButton *leg; +}; + +#endif diff --git a/seaset.cpp b/seaset.cpp new file mode 100644 index 0000000..e1c6231 --- /dev/null +++ b/seaset.cpp @@ -0,0 +1,1901 @@ +#include "seaset.h" +#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 seaStart[64]; + 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(seaStart, sizeof seaStart, "ssh %s@%s sea start", instr, host); + } else { + snprintf(seaStart, sizeof seaStart, "ssh l_samenv@%s sea start %s", host, instr); + } + sc->startServer = seaStart; + } + */ +} + +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); +} diff --git a/seaset.h b/seaset.h new file mode 100644 index 0000000..6c8a244 --- /dev/null +++ b/seaset.h @@ -0,0 +1,228 @@ +#ifndef SEASET_H +#define SEASET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sicsconn.h" +#include "seaplot.h" + +#define SEA_MAX_RANGE (240*3600-60) + +// values lower or equal than this value are considered as undefined +#define DATA_UNDEF (-1e33) + +class SeaPlot; +class SeaSet; +class SeaRow; +class QGrid; +class LegendButton; + +class SeaData { +public: + SeaData(const QString &name, const QString &label, const QString &plotName, int color=0); + ~SeaData(); + int addValue(double xx, QString value); + bool update(); + void init(time_t step, int siz); + void showValueAt(double at); + bool isActive(); + + QString name; + QString label; + QString plotName; + int size; + double *x; // x[0..size_m-1] + double *y; // y[0..size_m-1] + double step_m; + double lastx; + int size_m; + bool clr; + bool dirty; + bool modified; + bool bold; + bool exact; + double period; // update period + long key; + SeaPlot *plot; + SeaRow *row; + QLabel *shownValue; + int decipos; + long style; +}; + +typedef enum {hiddenRow, shownRow, newRow} RowState; + +class SeaRow: public QScrollView { + Q_OBJECT +public: + SeaRow(QWidget *parent); + QHBox *hBox; +// QScrollView *left; + SeaPlot *plot; + QGrid *leg; + QScrollView *legScroll; + QVBox *right; + SeaSet *set; + QPtrList legendList; + uint usedLegend; + QHBox *bBox; + RowState state; + QPushButton *lBut; + QPixmap pmLin, pmLog; + QLabel *tagLabel; + QLabel *timeLabel; + void setInnerHeights(bool isfirstrow, int rowH); + void setTag(QString tag); + + virtual void resizeEvent(QResizeEvent *e); + virtual QSize minimumSizeHint() const; +public slots: + void hideRow(); + void linLog(); +}; + +typedef enum {liveOff, liveAuto, liveOn} LiveState; + +typedef struct GetArg { + bool all, append; + time_t from, to; +} GetArg; + +typedef struct AutoArg { + QString vars; +} AutoArg; + +class SeaSet : public QObject { + Q_OBJECT +friend class SeaPlot; +friend class SeaRow; +public: + SeaSet( QSplitter *parent, long range, const char *name); + SeaRow *newPlot(const char *name = ""); + int leftLabelWidth(int in); + void insertData(const QString &cName, const QString &label, const QString &pName, const int color); + void clrDataList(); + void finishDataList(); + QString dateLabel(long dayOffset); + QPtrList dataList; + void readNewReplot(); + void rescaleRange(time_t range); + void rescale(time_t from, time_t to); + void rescale(double from, double to); + void calcBase(time_t from); + void saveRange(); + void saveRange(SeaPlot *plot, QwtDoubleRect &old, bool yAuto); + void hideRow(); + void adjustSizes(); + int sicsCommand(QString &cmd, int graph=0); // blocking + int sendSicsCommand(QString &cmd, int graph=0); // non-blocking + void setMarker(double x); + void setTimeLabel(double x); + void runningMarkerOff(bool beforeEvent); + void saveRowHeight(SeaRow *row, int height); + int getRowHeight(SeaRow *row); + void saveBoldState(SeaData *data); + void getBoldState(SeaData *data); + void setFirstRow(); + void gotoTime(double at, bool silent); + + SicsConnection *sc, *uc, *gc, *ec; + int legendWidth, actLegendWidth; + QString hostport; + time_t startRange, endRange; + time_t base; + double markerPos; + bool markerWasOn; + bool markerFix; + SeaRow *firstRow; + double lastTime; + +public slots: + void autoCurves(const char *vars = 0); + void setHost(const QString &hostport); + void setTimeRange(time_t start, long seconds); + void showAll(); + void liveUpdate(); + //void handleSics(const char *line); + void shiftLeft(); + void zoomOutX(); + void shiftRight(); + void undoZoom(); + void setLive(bool on); + void restart(const char *vars); + void replot(); + void replotAnyway(); + void asyncHandler(); + void setMarker(time_t t); + +signals: + void putCurve(const QString &name, const QString &plot); + void undoEnabled(bool on); + void updateCurveList(); + void gotoTime(time_t from, time_t at, time_t to, bool silent); + +private: + void setBase(); + void setDate(); + void getCurves(bool all, time_t from, time_t to); + SeaData *findData(const QString &name); + void calcLeftLabelWidth(); + + bool autoCurvesP1(); + void autoCurvesP2(); + int getCurvesP1(); + void getCurvesP2(); + + QSplitter *split; + QLayout *layout; + time_t lastRead; // time of last read on server + bool compressed; + QPtrList rows; + int nplots; + QwtPlotZoomer *zoom; + int labelWidth; + int labelMode; + bool reread; + int refresh; + LiveState live; + time_t liveTime; + SeaPlot *undoPlot; + QwtDoubleRect undoRect; + time_t undoStart, undoEnd; + bool undoAuto; + bool initSizes; + int xSize; + time_t curveStep; + // asynchronous stuff: + enum {async_idle, async_auto, async_get} async_mode; + AutoArg autoArg, autoArgDo; + GetArg getArg, getArgDo; + bool autoDo, getDo, getNewData; + QTimer *asyncTimer; + QTime meas; + QTime tmot; + bool showPlot; + QDict rowHeightDict; + QDict boldDict; +}; + +void setprintit(int p); +int printit(void); + +#endif diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..4f05e2d --- /dev/null +++ b/settings.cpp @@ -0,0 +1,339 @@ +#include "settings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +void SetLength(QLineEdit *lineEdit, int min, int max) { + lineEdit->setMaxLength(max); + int w0 = lineEdit->fontMetrics().width('0'); + int w = lineEdit->frameWidth() * 2 + 6; + lineEdit->setMinimumWidth(w + w0 * min); + lineEdit->setMaximumWidth(w + w0 * max); +} + +Settings::Settings(QWidget* parent) + : QWidget(parent) +{ + QHBoxLayout *buttonLayout; + QVBoxLayout *vbox; + QString str; + time_t now; + struct tm date; + + vbox = new QVBoxLayout(this, 0, 0, "vBox"); + + setName("Settings"); + + table = new QScrollView(this, "table"); + form = new QWidget(table->viewport(), "form"); + formVL = new QVBoxLayout(form, 0, 0, "formVL"); + formLayout = new QGridLayout(0, 3, 3, 6, 3); + formVL->addLayout(formLayout); + formVL->addStretch(1); + /* + formBox = new QHBox(table->viewport(), "formBox"); + form = new QGrid(4, formBox, "form"); + */ + table->addChild(form); + table->setHScrollBarMode(QScrollView::AlwaysOff); + + buttonLayout = new QHBoxLayout(0, 3, 3, "buttonLayout"); + + newButton = new QPushButton(this, "newButton"); + newButton->setText("New Curve"); + buttonLayout->addWidget(newButton); + + /* + autoButton = new QPushButton(this, "autoButton"); + autoButton->setText("Auto"); + buttonLayout->addWidget(autoButton); + */ + + cancelButton = new QPushButton(this, "cancelButton"); + cancelButton->setText("Cancel"); + buttonLayout->addWidget(cancelButton); + + okButton = new QPushButton(this, "okButton"); + okButton->setText("OK"); + buttonLayout->addWidget(okButton); + okButton->setDefault(true); + + buttonLayout->addStretch(1); + time(&now); + date = *localtime(&now); + + buttonLayout->addWidget(new QLabel("# Days", this)); + rLE = new QLineEdit(this); + SetLength(rLE, 2, 2); + rLE->setText(str); + rLE->setValidator(new QIntValidator(0, 99, this)); + buttonLayout->addWidget(rLE); + + buttonLayout->addWidget(new QLabel("starting from", this)); + dLE = new QLineEdit(this); + SetLength(dLE, 2, 2); + str.sprintf("%d", date.tm_mday); + dLE->setText(str); + dLE->setValidator(new QIntValidator(0, 31, this)); + buttonLayout->addWidget(dLE); + + buttonLayout->addWidget(new QLabel(".", this)); + mLE = new QLineEdit(this); + SetLength(mLE, 2, 2); + str.sprintf("%d", date.tm_mon+1); + mLE->setText(str); + mLE->setValidator(new QIntValidator(1, 12, this)); + buttonLayout->addWidget(mLE); + + buttonLayout->addWidget(new QLabel(".", this)); + yLE = new QLineEdit(this); + SetLength(yLE, 4, 4); + str.sprintf("%d", date.tm_year+1900); + yLE->setText(str); + yLE->setValidator(new QIntValidator(2001, date.tm_year+1900, this)); + buttonLayout->addWidget(yLE); + + hrLE = new QLineEdit(this); + SetLength(hrLE, 5, 5); + hrLE->setText("00:00"); + buttonLayout->addWidget(hrLE); + + vbox->addLayout(buttonLayout); + vbox->addWidget(table, 1); + //vbox->addStretch(1); + + formLayout->addWidget(new QLabel("Curve", form), 0, 0); + formLayout->addWidget(new QLabel("Label", form), 0, 1); + formLayout->addWidget(new QLabel("Unit", form), 0, 2); + formLayout->addWidget(new QLabel("Color", form), 0, 3); + + /* + new QLabel("Curve", form); + new QLabel("Label", form); + new QLabel("Plot", form); + new QLabel("Color", form); + */ + + row = 1; + + connect(newButton, SIGNAL(clicked()), + this, SLOT(addEmpty())); + connect(okButton, SIGNAL(clicked()), + this, SLOT(ok())); + /* + connect(autoButton, SIGNAL(clicked()), + this, SLOT(autoCurves())); + */ + connect(cancelButton, SIGNAL(clicked()), + this, SLOT(cancel())); + + connect(rLE, SIGNAL(textChanged(const QString &)), + this, SLOT(checkRange(const QString &))); + /* + connect(dLE, SIGNAL(textChanged(const QString &)), + this, SLOT(calcDate(const QString &))); + connect(mLE, SIGNAL(textChanged(const QString &)), + this, SLOT(calcDate(const QString &))); + connect(yLE, SIGNAL(textChanged(const QString &)), + this, SLOT(calcDate(const QString &))); + */ + clearWState(WState_Polished); +} + + +void Settings::updateHeight() { + form->setMinimumHeight(table->visibleHeight()); +} + +void Settings::resizeEvent(QResizeEvent *e) { + updateHeight(); + form->setFixedWidth(table->visibleWidth()); + QWidget::resizeEvent(e); +} + +void Settings::showIt(bool yes) { + if (yes) { + clear(); + putCurves(&set->dataList); + show(); + } else { + hide(); + } +} + +#define YEAR (24*3600*360) + +void Settings::checkRange(const QString &str0) { + if (str0.compare("") == 0) { + dLE->setEnabled(false); + mLE->setEnabled(false); + yLE->setEnabled(false); + hrLE->setEnabled(false); + } else { + dLE->setEnabled(true); + mLE->setEnabled(true); + yLE->setEnabled(true); + hrLE->setEnabled(true); + } +} + +void Settings::putCurve(const QString &name, const QString &label, + const QString &plotName, long colorIndex) { + QLineEdit *n, *p; + ColorMenu *cm; + unsigned int idx; + QString colortext; + + idx = row - 1; + + if (idx < names.count()) { + n = names.at(idx); + } else { + n = new QLineEdit(form); + SetLength(n, 8, 64); + formLayout->addWidget(n, row, 0); + names.append(n); + } + n->setText(name); + n->show(); + + if (idx < labels.count()) { + p = labels.at(idx); + } else { + p = new QLineEdit(form); + SetLength(p, 8, 64); + formLayout->addWidget(p, row, 1); + labels.append(p); + } + p->setText(label); + p->show(); + + if (idx < plots.count()) { + p = plots.at(idx); + } else { + p = new QLineEdit(form); + SetLength(p, 4, 8); + formLayout->addWidget(p, row, 2); + plots.append(p); + } + p->setText(plotName); + p->show(); + + Convert2ColorName(colorIndex, colortext); + if (idx < colors.count()) { + cm = colors.at(idx); + cm->setCurrentItem(0); + cm->setCurrentText(colortext); + } else { + cm = new ColorMenu(colortext, form); + cm->setLength(12, 32); + formLayout->addWidget(cm, row, 3); + colors.append(cm); + } + cm->show(); + + row++; +} + + +void Settings::putCurves(QPtrList *dataList) { + SeaData *c; + for (c = dataList->first(); c != 0; c = dataList->next()) { + putCurve(c->name, c->label, c->plotName, c->style); + } + updateHeight(); +} + +void Settings::addEmpty() { + putCurve("","","",-1); + updateHeight(); +/* + names.last()->show(); + labels.last()->show(); + plots.last()->show(); + colors.last()->show(); +*/ +} + +void Settings::apply() { + int i, n; + long days; + time_t t; + struct tm date; + QStringList hr_min; + + if (rLE->text().compare("") != 0) { + days = rLE->text().toLong(); + + time(&t); + date = *localtime(&t); + date.tm_mday = dLE->text().toInt(); + date.tm_mon = mLE->text().toInt() - 1; + date.tm_year = yLE->text().toInt() - 1900; + hr_min = QStringList::split(":", hrLE->text()); + date.tm_hour = (int)hr_min[0].toFloat(); + if (hr_min.size() > 1) { + date.tm_min = (int)hr_min[1].toFloat(); + } else { + date.tm_min = 0; + } + date.tm_sec = 0; + date.tm_isdst = -1; + + set->setTimeRange(mktime(&date), 24*3600*QMAX(1, days)); + set->autoCurves(); + } else { + set->clrDataList(); + n = names.count(); + for (i = 0; i < n; i++) { + //printf("%s\n", names.at(i)->text().latin1()); + set->insertData(names.at(i)->text(), labels.at(i)->text(), plots.at(i)->text(), colors.at(i)->getColorIndex()); + } + set->finishDataList(); + } +} + +void Settings::ok() { + apply(); + leaveSettings(); +} + +void Settings::cancel() { + leaveSettings(); +} + +void Settings::clear() { + int i, n; + n = names.count(); + for (i = 0; i < n; i++) { + names.at(i)->setText(""); + plots.at(i)->setText(""); + labels.at(i)->setText(""); + colors.at(i)->setValue(0); + } + row = 1; + rLE->setText(""); +} + +void Settings::autoCurves() { + clear(); + set->autoCurves(); + putCurves(&set->dataList); +} + + diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..eee4fb5 --- /dev/null +++ b/settings.h @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#include "seaset.h" +// for ColorMenu: +#include "device.h" + +class Settings : public QWidget +{ + Q_OBJECT + +public: + Settings(QWidget* parent = 0); + //~Settings(); + + void clear(); + void putCurves(QPtrList *curves); + void apply(); + SeaSet *set; + QPushButton *okButton, *cancelButton, *newButton; + /* QPushButton *autoButton; */ + QLineEdit *rLE, *dLE, *mLE, *yLE, *hrLE; +signals: + void leaveSettings(); +public slots: + void addEmpty(); + void ok(); + void cancel(); + void autoCurves(); + void checkRange(const QString &str); + void showIt(bool show); + void resizeEvent(QResizeEvent *e); +protected: + void putCurve(const QString &name, const QString &label, const QString &plotName, long colorIndex); + void updateHeight(); + QScrollView *table; + QWidget *form; + QVBoxLayout *formVL; + QGridLayout *formLayout; + QPtrList names; + QPtrList labels; + QPtrList plots; + QPtrList colors; + int row; + +}; + + diff --git a/sicsconn.cpp b/sicsconn.cpp new file mode 100644 index 0000000..43b5f0c --- /dev/null +++ b/sicsconn.cpp @@ -0,0 +1,601 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sicsconn.h" +#include "instr_hosts.h" +#include +#include +#include +#include +#include + + +int printit(void); + +int uselect(int nfds, + fd_set * readfds, fd_set * writefds, fd_set * exceptfds, + struct timeval *timeout) +{ + + sigset_t sigmask; + struct timespec tmo, *tmoPtr; + int result; + + sigfillset(&sigmask); + if (timeout) { + tmo.tv_sec = timeout->tv_sec; + tmo.tv_nsec = timeout->tv_usec * 1000; + tmoPtr = &tmo; + } else { + tmoPtr = NULL; + } + result = pselect(nfds, readfds, writefds, exceptfds, tmoPtr, &sigmask); + return result; +} + +static int sigpipe_ignored = 0; + +int SicsSockAdr(struct sockaddr_in *sockaddr, const char *hostport, + int defaultPort, const char *server) { + // create sockadr from hostport (hostname and port number separated with colon) + // hostport may also be an instrument name, in this case server has to be defined + + struct hostent *hostent; /* Host database entry */ + struct in_addr addr; /* For 64/32 bit madness */ + char *p; + char host[128], instr[32]; + int port; + int l; + + (void) memset((char *) sockaddr, '\0', sizeof(struct sockaddr_in)); + if (hostport == NULL) hostport=""; + p = strchr((char *)hostport, ':'); + if (p != NULL) { + l = p - hostport; + port = atoi(p+1); + } else { + InstrHost((char *)server, (char *)hostport, instr, sizeof instr, + host, sizeof host, &port); + if (port == 0) { + port = atoi(hostport); + if (port != 0) { + l = 0; + } else { + l = strlen(hostport); + } + } else { + l = -1; + } + } + if (l>= 0) { + if (l >= (int)sizeof host) return -1; + strncpy(host, hostport, l); + host[l]='\0'; + } + if (port == 0) port = defaultPort; + if (port == 0) return -1; + + sockaddr->sin_family = AF_INET; + sockaddr->sin_port = htons((unsigned short) (port & 0xFFFF)); + + if (host[0]=='\0') { + addr.s_addr = INADDR_ANY; + } else { + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &addr, + (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); + } else { + addr.s_addr = inet_addr(host); + if (addr.s_addr == (unsigned long)-1) { + return -1; /* error */ + } + } + } + /* + * There is a rumor that this assignment may require care on + * some 64 bit machines. + */ + sockaddr->sin_addr.s_addr = addr.s_addr; + return 0; +} + +int SicsConnect(const char *hostport, const char *server, bool async) { + struct sockaddr_in sadr; + int iret, fd; + int oldopts; + + iret = SicsSockAdr(&sadr, hostport, 8641, server); + if (iret < 0) return -1; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) return -1; + if (async) { + oldopts = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, oldopts | O_NONBLOCK); + } + iret = connect(fd, (struct sockaddr *)&sadr, sizeof(sadr)); + if (iret < 0) { + switch (errno) { + case EINPROGRESS: + case EALREADY: + case EISCONN: + return fd; + } + return -1; + } + return fd; +} + +int SicsConnectSuccess(int fd) +{ + // returns 1 on success, 0 when pending, -1 on error + fd_set wmask, rmask; + struct timeval tmo = { 0, 0 }; + int oldopts; + int ret; + + oldopts = fcntl(fd, F_GETFL, 0); + assert(oldopts | O_NONBLOCK); /* fd must be in non-blocking mode */ + + FD_ZERO(&wmask); + FD_ZERO(&rmask); + FD_SET(fd, &wmask); + FD_SET(fd, &rmask); + ret = uselect(fd + 1, &rmask, &wmask, NULL, &tmo); + if (ret > 0) { + if (FD_ISSET(fd, &rmask)) { /* there may already be data for read */ + if (recv(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */ + ret = -1; /* first recv failed */ + } + } else { + if (FD_ISSET(fd,&wmask)) { + if (send(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */ + ret = -2; /* first send failed */ + } + } + } + } + fcntl(fd, F_SETFL, oldopts & ~O_NONBLOCK); /* reset to blocking mode */ + return ret; +} + +SicsConnection::SicsConnection(QObject *parent) : QObject(parent) { + hostport = NULL; + userpass = NULL; + server = NULL; + state = disconnected; + bufline = ""; + bufstate = buf_got_line; + connect_state = connect_start; + command2send = ""; + tmo = 0; + if (!sigpipe_ignored) { + signal (SIGPIPE, SIG_IGN); + sigpipe_ignored = 1; + } + rdbuffer = ""; + progressPos = 0; + rdpos = 0; + debug = 0; +} + +//SicsConnection::SicsConnection(SicsConnection *copy) { +// hostport = strdup(copy->hostport); +// userpass = NULL; +// state = disconnected; +//} + +SicsConnection::~SicsConnection() { + if (state != disconnected) { + close(fd); + fd = -1; + } + if (hostport) free(hostport); + if (userpass) free(userpass); + if (server) free(server); +} + +void SicsConnection::setHost(const char *hostport, const char *server) { + if (this->hostport != NULL) free(this->hostport); + if (hostport != NULL) { + this->hostport = strdup(hostport); + } else { + this->hostport = NULL; + } + if (this->server != NULL) free(this->server); + this->server = strdup(server); +} + +void SicsConnection::setUser(const char *userpass) { + if (this->userpass != NULL) free(this->userpass); + if (userpass != NULL) { + this->userpass = strdup(userpass); + } else { + this->userpass = NULL; + } +} + +int SicsConnection::handleBuffer(void) { + return handleBuffer(0); +} + +int SicsConnection::handleBuffer(int tmo) { + fd_set mask; + struct timeval tmov; + int iret = 1; + int pos; + char ch; + char line[128]; + + switch (connect_state) { + case connect_start: + fd = SicsConnect(hostport, server, true); + if (fd < 0) { + connect_state = connect_error; // what else? + return -1; + } + connect_state = connect_wait; + return 0; + case connect_wait: + iret = SicsConnectSuccess(fd); + if (iret <= 0) { + if (startServer) { + iret = system(startServer); + if (iret) { + printf("can not connect to sea and failed to start the sea server\n"); + printf("\n"); + pos = strcspn(hostport, ":"); + printf("please execute 'sea start' from the instrument account of %.*s\n", pos, hostport); + printf("\n"); + exit(1); + } + } else { + iret = 1; + } + startServer = NULL; + return iret; + } + connect_state = connect_login; + return 0; + case connect_login: + if (userpass == NULL) { + snprintf(line, sizeof line, "Spy 007\n"); + } else { + snprintf(line, sizeof line, "%s", userpass); + } + bufstate = buf_sent_line; + send(fd, line, strlen(line), 0); + connect_state = connect_waitlogin; +// if (strcmp(server, "sea") == 0 && userpass != NULL) { +// printf("*** send login (%s %s)\n", server, line); +// } + return 0; + case connect_error: + return -2; + default: break; + } + + FD_ZERO(&mask); + while (bufstate > buf_got_finished) { + do { + FD_SET(fd, &mask); + tmov.tv_sec = tmo; + tmov.tv_usec = 0; + iret = select(fd+1, &mask, NULL, NULL, &tmov); + if (iret < 0) { + perror("select"); + close(fd); + fd = -1; + connect_state = connect_error; + state = disconnected; + return -2; + } else if (iret == 0) { + return -1; /* no more data */ + } + iret = recv(fd, &ch, 1, 0); + if (iret <= 0) { + close(fd); + fd = -1; + connect_state = connect_error; + state = disconnected; + if (printit()) printf("zero on receive -> disconnected\n"); +// printf("*** disconnected (zero on receive)\n"); + return -2; + } + bufline.append(ch); + } while (ch != '\n'); + iret = 0; + rdbuffer += bufline; + if (debug) { + // printf("%s bufline{%s}\n", debug, bufline.latin1()); + } + if ((int)rdbuffer.length() >= progressPos + 1000) { + progress(rdbuffer.length() - progressPos); + progressPos = rdbuffer.length(); + } +// if (strcmp(server, "sea") == 0 && userpass != NULL) { +// printf("*** bufline %s", bufline.latin1()); +// } + if (bufstate == buf_got_start && bufline.startsWith("TRANSACTIONFINISHED")) { + bufstate = buf_got_finished; + iret = 1; + } + if (bufstate == buf_sent_transact && bufline.startsWith("TRANSACTIONSTART")) { + bufstate = buf_got_start; + } + if (bufstate == buf_sent_line) { + bufstate = buf_got_line; + if (connect_state == connect_waitlogin) { + if (debug) { + printf("connectlogin %s\n", bufline.latin1()); + } + if (bufline.startsWith("Login OK\n")) { + connect_state = connect_done; + state = connected; + if (command2send != "") { + sendThis(command2send.latin1()); + command2send = ""; + if (state == disconnected) { + return -1; + } + bufstate = buf_sent_transact; + } + } else { + bufstate = buf_sent_line; + } + } else { + iret = 1; + } + } + bufline = ""; + } + + return iret; +} + +int SicsConnection::getLine(QString &line) { + QString qline; + int pos, len; + + if (state == disconnected) { + return -1; + } + handleBuffer(0); + if (rdpos >= (int)rdbuffer.length() && tmo > 0) { + if (handleBuffer(tmo) < 0) { + printf("timeout %d\n", tmo); + } + } + if (rdpos < (long)rdbuffer.length()) { + pos = rdbuffer.find('\n', rdpos); + if (pos < rdpos) { // lf not found, return up to end of buffer (is this correct?) + return 0; /***/ + len = rdbuffer.length() - rdpos; + line = rdbuffer.mid(rdpos); + progressPos -= rdbuffer.length(); + rdbuffer=""; + rdpos = 0; + } else { + len = pos - rdpos; + line = rdbuffer.mid(rdpos,len); + rdpos = pos + 1; + //rdbuffer.remove(0, pos + 1); + } + if (debug) { + //printf("%s getLine{%s}{%s}\n", debug, line.latin1(), rdbuffer.mid(rdpos,9).latin1()); + printf("%s getLine{%s}\n", debug, line.latin1()); + } + if (line.startsWith("TRANSACTIONFINISHED")) { + if (state == replying) { + state = connected; + return 0; + } + } + return len; + } else { + if (debug) { + // printf("%s error\n", debug); + } + return -1; + } + return 0; +} + +void SicsConnection::swallow() { + QString line; + bool done; + + if (debug) { + printf("%s swallow start\n", debug); + } + while (state == replying) { + if (getLine(line) > 0) { + done = false; + handle(line, &done); + if (!done) { + //printf("*** swallow {%s}\n", line); + } + } + } + if (debug) { + printf("%s swallow end\n", debug); + } +} + +int SicsConnection::sendThis(const char *str) { + int l, iret; + if (state != disconnected) { + l = strlen(str); +// if (strcmp(server, "sea") == 0 && userpass != NULL) { +// printf("*** send {%s}\n", str); +// } + iret = send(fd, str, l, 0); + if (iret != l) { + printf("sent %d of %d, disconnect\n", iret, l); + close(fd); + fd = -1; + state = disconnected; + return -1; + } + return 1; + } + return -1; +} + +void SicsConnection::reconnect(void) { + if (state != disconnected) { + close(fd); + fd = -1; + state = disconnected; + } + connect_state = connect_start; + command2send.sprintf("fulltransact config listen 1\n"); +} + +void SicsConnection::disconnect(void) { + close(fd); + fd = -1; + state = disconnected; +} + +int SicsConnection::sendCommand(const char *cmd) { + char *nl; + + if (debug) { + printf("%s swallow before %s\n", debug, cmd); + } + swallow(); + if (debug) { + printf("%s sendCommand %s\n", debug, cmd); + } + nl = strrchr((char *)cmd, '\n'); /* check if we have to add a final line break */ + if (nl == NULL || nl[1] != '\0') { + command2send.sprintf("fulltransact %s\n", cmd); + } else { + command2send.sprintf("fulltransact %s", cmd); + } + + if (state == disconnected) { +// printf("*** reconnect!\n"); + connect_state = connect_start; + } else { + sendThis(command2send.latin1()); + bufstate = buf_sent_transact; + command2send = ""; + if (state == disconnected) { + return -1; + } + } + return 1; +} + +int SicsConnection::getResponse() { // get BEGINNING of response + int iret; + QString line; + + state = connected; + tmo = 10; + while (1) { + iret = getLine(line); + if (iret <= 0) { + if (iret == -2) { + if (printit()) printf("timeout on sea command\n"); + } + //printf("error in getLine %d\n", iret); + if (debug) { + printf("%s error in getResponse {%s}\n", debug, rdbuffer.latin1() + rdpos); + } + return iret; + } + if (line.startsWith("TRANSACTIONSTART")) break; + if (debug) { + printf("%s getResponse handle %s\n", debug, line.latin1()); + } + handle(line.latin1()); + }; + state = replying; + if (debug) { + printf("%s gotResponse\n", debug); + } + return 1; +} + +int SicsConnection::command(const char *cmd) { + int iret; + int cnt, tries; + + tries = 3; + do { + cnt = 5; + while (cnt > 0 && (connect_state != connect_done || bufstate > buf_got_finished)) { + handleBuffer(1); + cnt--; + } + iret = sendCommand(cmd); + handleBuffer(5); + iret = getResponse(); + tries--; + } while (iret < 0 && tries > 0); + return iret; +} + +int SicsConnection::handleMessages(int tmoArg) { + int iret; + QString line; + bool bs=false; + + tmo = tmoArg; + if (bufstate <= buf_got_finished) { + bufstate = buf_sent_line; + bs = true; + } + iret = getLine(line); + tmo = 0; + while (iret >= 0) { + if (iret > 0) { + handle(line.latin1()); + } + iret = getLine(line); + } + if (bs && bufstate == buf_sent_line) { + bufstate = buf_got_line; + } + return iret; +} + +#ifdef TESTSC + +#include + +int main(void) { + SicsConnection *sc; + char line[256]; + char *lin; + int iret; + + sc = SicsNewConnection("lnsl15", 13006); + if (sc == NULL) return -1; + while (1) { + printf("> "); + lin = fgets(line, sizeof line, stdin); + if (lin == NULL) return 1; + iret = SicsCommand(sc, line); + if (iret < 0) return 2; + while (1) { + iret = getLine(line); + if (iret == 0) break; + if (iret < 0) return 3; + if (iret > 0) { + printf("(%s)\n", line.latin1()); + } + } + } +} + +#endif diff --git a/sicsconn.h b/sicsconn.h new file mode 100644 index 0000000..e7fca9d --- /dev/null +++ b/sicsconn.h @@ -0,0 +1,72 @@ +#ifndef SICSCONN_H +#define SICSCONN_H + +#include +#include + +typedef enum { disconnected, connected, replying } ConnState; + +class SicsConnection : public QObject { + Q_OBJECT +public: + SicsConnection(QObject *parent); + ~SicsConnection(); + void setHost(const char *hostport, const char *server); + void setUser(const char *userpass); + int command(const char *cmd); + /* send a command (and connect before, if necessary) + return values: 1 on success + -1 on error (no connection) + 0 on empty response (should not happen) + */ + + int sendCommand(const char *cmd); + // send, but do not wait + + int getResponse(); + // get beginning of response + + int getLine(QString &line); + /* get answer from command + the return value is: + - the number of characters read (excluding line break, but at least 1) + - 0 when the response is at its end + - negative on error + */ + int handleMessages(int tmo); + /* handle pending messages */ + + int handleBuffer(int tmo); + void swallow(); + char *debug; + ConnState state; + char *startServer = NULL; + +signals: + void handle(const char *line, bool *done = NULL); + void reconnected(void); + void progress(int bytes); + +public slots: + void reconnect(void); + void disconnect(void); + int handleBuffer(void); + +private: + int sendThis(const char *cmd); + int fd; + char *hostport; + char *userpass; + char *server; + int tmo; + enum {buf_got_line, buf_got_finished, buf_sent_line, buf_got_start, buf_sent_transact} bufstate; + enum {connect_done, connect_start, connect_wait, connect_login, connect_waitlogin, connect_error} connect_state; + QString command2send; + QString bufline; + QString rdbuffer; + int rdpos; + int progressPos; + long tim; +}; + +#endif diff --git a/tab.cpp b/tab.cpp new file mode 100644 index 0000000..415fc25 --- /dev/null +++ b/tab.cpp @@ -0,0 +1,267 @@ +#include "tab.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum {GRAPH, SETTINGS, DEVICE, COMMAND}; + +void Tab::checkSplit(SplitNo splitNo) { + assert(widgetno == splitNo); + widgetno++; +} + +//Tab::Tab(const char* instr, QWidget *parent, long range, const char *warning) +// : QDialog(parent, "tab", false, Qt::WType_TopLevel | Qt::WStyle_Minimize) +Tab::Tab(const char* instr, QWidget *parent, long range, const char *warning) + : QMainWindow(parent, "tab", Qt::WType_TopLevel | Qt::WStyle_Minimize) +{ + //QBoxLayout *vbox; + QApplication::style().polish(this); + + actFocus = -1; + + //vbox = new QVBoxLayout(this, 0, 0, "vbx"); + split = new QSplitter(Qt::Horizontal, this); + setCentralWidget(split); + //vbox->addWidget(split); + + widgetno = 0; + leftWidgetNo = graph_split; + rightWidgetNo = right_split; + + graph = new Graph(instr, split, range); + if (graph->set == NULL) { + graph = NULL; + return; + } + checkSplit(graph_split); + + graph->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 1, 1)); + connect(graph, SIGNAL(gotoSettings()), this, SLOT(gotoSettings())); + settings = new Settings(split); + settings->set = graph->set; + connect(settings, SIGNAL(leaveSettings()), + this, SLOT(leaveSettings())); + settings->hide(); + checkSplit(settings_split); + + rightSplit = new QSplitter(Qt::Vertical, split, "rightSplit"); + rightSplit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 1, 1)); + checkSplit(right_split); + + if (warning[0]) { + foreignwarn = new QVBox(rightSplit); + warnlabel = new QLabel(warning, foreignwarn); + showparbutton = new QPushButton("show parameters", foreignwarn); + QSize siz = foreignwarn->minimumSizeHint(); + foreignwarn->setFixedHeight(siz.height()); + warnlabel->setEraseColor(QColor(255,127,0)); + } + + device = new Device(rightSplit); + if (warning[0]) { + device->hide(); + connect(showparbutton, SIGNAL(clicked()), this, SLOT(showPars())); + } + + command = new Command(rightSplit, "Command", graph->set->uc); + connect(command->cmd, SIGNAL(returnPressed()), device, SLOT(update())); + connect(command, SIGNAL(activate()), device, SLOT(activate())); + device->init(command, graph->set); + + QTimer *timer = new QTimer(this, "repeat"); + connect(timer, SIGNAL(timeout()), this, SLOT(handler())); + timer->start(100); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + exportXY = new Export(graph->set, split, instr); + exportXY->hide(); + exportXY->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 1, 1)); + connect(exportXY, SIGNAL(finish()), this, SLOT(finishExport())); + connect(graph, SIGNAL(openExport()), this, SLOT(openExport())); + connect(graph->set, SIGNAL(updateCurveList()), exportXY, SLOT(updateCurveList())); + checkSplit(export_split); + + report = new Report(split, graph->set->ec); + report->hide(); + checkSplit(report_split); + connect(report, SIGNAL(leave()), this, SLOT(leaveReport())); + connect(report, SIGNAL(openMe()), this, SLOT(openReport())); + + message = new QLabel(split); + message->hide(); + + for (int i = 0; i < 2 * 3; i++) { + oldSizes[i] = 0; + } +} + +bool Tab::shown(QWidget *w) { + return w->isShown() && w->width() > 3 && w->height() > 3; +} + +void Tab::showPars() { + QValueList splitsizes; + int h; + + showparbutton->hide(); + QSize siz = warnlabel->minimumSizeHint(); + foreignwarn->setFixedHeight(siz.height()); + device->show(); + splitsizes = rightSplit->sizes(); + h = (splitsizes[1] + splitsizes[2]) / 2; + splitsizes[1] = h; + splitsizes[2] = h; + rightSplit->setSizes(splitsizes); + device->update(); +} + +bool Tab::splitChange(SplitNo splitNo) { + int lw = 0, rw = 0; + int i; + int tot; + float s; + + sizes = split->sizes(); + lw = sizes[leftWidgetNo]; + rw = sizes[rightWidgetNo]; + i = leftWidgetNo + 2 * (rightWidgetNo - right_split); + if (splitNo < right_split) { + if (splitNo == leftWidgetNo) return FALSE; + leftWidgetNo = splitNo; + } else { + if (splitNo == rightWidgetNo) return FALSE; + rightWidgetNo = splitNo; + } + tot = lw + rw; + if (rw > 1 && lw > 1) { + oldSizes[i] = lw / float(tot); + } + s = oldSizes[leftWidgetNo + 2 * (rightWidgetNo - right_split)]; + if (s != 0) { + lw = s * tot + 0.5; + rw = tot - lw; + } + for (i = 0; i < n_split; i++) { + sizes[i] = 0; + } + sizes[leftWidgetNo] = lw; + sizes[rightWidgetNo] = rw; + return TRUE; +} + +void Tab::gotoSettings() { + if (splitChange(settings_split)) { + graph->showIt(false); + settings->showIt(true); + split->setSizes(sizes); + } +} + +void Tab::leaveSettings() { + if (splitChange(graph_split)) { + settings->showIt(false); + graph->showIt(true); + } + split->setSizes(sizes); +} + +void Tab::openExport() { + if (splitChange(export_split)) { + rightSplit->hide(); + exportXY->showMe(); + split->setSizes(sizes); + } +} + +void Tab::finishExport() { + if (splitChange(right_split)) { + exportXY->hide(); + rightSplit->show(); + split->setSizes(sizes); + } +} + +void Tab::openReport() { + if (splitChange(report_split)) { + rightSplit->hide(); + report->show(); + split->setSizes(sizes); + } +} + +void Tab::leaveReport() { + if (splitChange(right_split)) { + report->hide(); + rightSplit->show(); + split->setSizes(sizes); + } +} + +void Tab::sleep(const char *text) { + graph->hide(); + rightSplit->hide(); + message->setText(text); + message->show(); +} + +void Tab::wake() { + graph->show(); + rightSplit->show(); + exportXY->hide(); + report->hide(); + message->hide(); + graph->set->sc->disconnect(); + graph->set->uc->disconnect(); + graph->set->gc->disconnect(); + graph->set->ec->disconnect(); + graph->set->sc->reconnect(); + /* uc is connected automatically with sc */ + graph->set->gc->reconnect(); + graph->set->ec->reconnect(); +} + +void Tab::initSize(int w, int h) { + QSize qs = QSize(w, h).expandedTo(split->minimumSizeHint()); + resize(qs); + sizes = split->sizes(); + sizes[0] = w/2; + sizes[2] = w/2; + split->setSizes(sizes); +} + +void Tab::handler() { + command->handler(); + if (shown(command) && command->cmd->hasFocus()) { + actFocus = COMMAND; + } + if (shown(graph) && graph->lineEditRange->hasFocus()) { + actFocus = GRAPH; + } + if (!shown(settings) && !shown(device)) { + if (actFocus == COMMAND) { + command->cmd->setFocus(); + } else { + if (!graph->lineEditRange->hasFocus()) { + graph->lineEditRange->setFocus(); + graph->lineEditRange->lineEdit()->selectAll(); + } + } + } +} + +void Tab::resizeEvent(QResizeEvent *e) { + //QDialog::resizeEvent(e); + QMainWindow::resizeEvent(e); +} diff --git a/tab.h b/tab.h new file mode 100644 index 0000000..a14c802 --- /dev/null +++ b/tab.h @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include "graph.h" +#include "settings.h" +#include "device.h" +#include "export.h" +#include "report.h" + +//class Tab : public QDialog +class Tab : public QMainWindow +{ + Q_OBJECT +public: + Tab(const char* instr, QWidget *parent, long range, const char *fromInstr); + void initSize(int w, int h); + void sleep(const char *text); + void wake(); + + Graph *graph; + Settings *settings; + Command *command; + Device *device; + Export *exportXY; + Report *report; + QSplitter *split; + QSplitter *rightSplit; + QLabel *message; + QVBox *foreignwarn; + QPushButton *showparbutton; + QLabel *warnlabel; + +public slots: + void leaveSettings(); + void gotoSettings(); + void handler(); + void openExport(); + void finishExport(); + void openReport(); + void leaveReport(); + void showPars(); + +signals: + void restart(); + +private: + int actFocus; + int watchSocket; + void resizeEvent(QResizeEvent *e); + bool shown(QWidget *w); + + enum SplitNo {graph_split, settings_split, right_split, export_split, report_split, n_split}; + + void checkSplit(SplitNo splitNo); + bool splitChange(SplitNo splitNo); + + int widgetno; + float oldSizes[2*3]; + SplitNo leftWidgetNo, rightWidgetNo; + QValueList sizes; +}; diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..a329255 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,462 @@ +#include "utils.h" +#include +#include +#include +#include + +void SetEraseColor(QWidget *widget, QWidget *from, bool inclBgr) { + const QPixmap *p; + p = from->erasePixmap(); + if (p) { + widget->setErasePixmap(*p); + if (inclBgr) { + widget->setPaletteBackgroundPixmap(*p); + } + } else { + widget->setEraseColor(from->eraseColor()); + if (inclBgr) { + widget->setPaletteBackgroundColor(from->eraseColor()); + } + } +} + +FlatButton::FlatButton(const QString &label, QWidget *parent, const char *name) + : QLabel(label, parent, name) { + x = -1; +} + +void FlatButton::mousePressEvent(QMouseEvent *e) { + x = e->x(); + y = e->y(); + mousePressed(); +} + +void FlatButton::mouseReleaseEvent(QMouseEvent *e) { + if (x >= 0 && abs(e->x() - x) + abs(e->y() - y) < 5) { + clicked(); + } + x = -1; +} + +void ShrinkButton(QPushButton *b) { + int margin; + QString text(b->text()); + b->setText("This is a long text"); + margin = b->sizeHint().width() - b->fontMetrics().width(b->text()); + b->setText(text); + b->setFixedWidth(b->fontMetrics().width(text) + margin); +} + +double DoubleTime(void) +{ + struct timeval now; + /* the resolution of this function is usec, if the machine supports this + and the mantissa of a double is 51 bits or more (31 for sec and 20 for milliseconds) + */ + gettimeofday(&now, NULL); + return now.tv_sec + now.tv_usec / 1e6; +} + +static char *coltab[N_SEACOLORS*2] = { + "X c #FFFFFF", "white", + "X c #FF0000", "red", + "X c #00FF00", "lime", // was green + "X c #0000FF", "blue", + "X c #FF00FF", "magenta", // fuchsia + "X c #FFFF00", "yellow", + "X c #00FFFF", "cyan", // aqua + "X c #000000", "black", + "X c #FFA500", "orange", + "X c #006400", "dark_green", + "X c #9400D3", "dark_violet", + "X c #A52A2A", "brown", + "X c #87CEEB", "sky_blue", + "X c #808080", "gray", + "X c #FF69B4", "hot_pink", + "X c #FFFFE0", "light_yellow", + "X c #00FF7F", "spring_green", + "X c #000080", "navy", + "X c #1E90FF", "dodger_blue", + "X c #9ACD32", "yellow_green", + "X c #008B8B", "dark_cyan", + "X c #808000", "olive", + "X c #DEB887", "burly_wood", + "X c #7B68EE", "medium_slate_blue", + "X c #483D8B", "dark_slate_blue", + "X c #98FB98", "pale_green", + "X c #FF1493", "deep_pink", + "X c #FF6347", "tomato", + "X c #32CD32", "lime_green", + "X c #DDA0DD", "plum", + "X c #7FFF00", "chartreuse", + "X c #800080", "purple", + "X c #00CED1", "dark_turquoise", + "X c #8FBC8F", "dark_sea_green", + "X c #4682B4", "steel_blue", + "X c #800000", "maroon", + "X c #3CB371", "medium_sea_green", + "X c #FF4500", "orange_red", + "X c #BA55D3", "medium_orchid", + "X c #2F4F4F", "dark_slate_gray", + "X c #CD853F", "peru", + "X c #228B22", "forest_green", + "X c #48D1CC", "medium_turquoise", + "X c #DC143C", "crimson", + "X c #D3D3D3", "light_gray", + "X c #ADFF2F", "green_yellow", + "X c #7FFFD4", "aquamarine", + "X c #BC8F8F", "rosy_brown", + "X c #20B2AA", "light_sea_green", + "X c #C71585", "medium_violet_red", + "X c #F0E68C", "khaki", + "X c #6495ED", "cornflower_blue", + "X c #556B2F", "dark_olive_green", + "X c #CD5C5C", "indian_red", + "X c #2E8B57", "sea_green", + "X c #F08080", "light_coral", + "X c #8A2BE2", "blue_violet", + "X c #AFEEEE", "pale_turquoise", + "X c #4169E1", "royal_blue", + "X c #0000CD", "medium_blue", + "X c #B8860B", "dark_goldenrod", + "X c #00BFFF", "deep_sky_blue", + "X c #FFC0CB", "pink", + "X c #4B0082", "indigo", + "X c #A0522D", "sienna", + "X c #FFD700", "gold", + "X c #F4A460", "sandy_brown", + "X c #DAA520", "goldenrod", + "X c #DA70D6", "orchid", + "X c #E6E6FA", "lavender", + "X c #5F9EA0", "cadet_blue", + "X c #D2691E", "chocolate", + "X c #66CDAA", "medium_aquamarine", + "X c #6B8E23", "olive_drab", + "X c #A9A9A9", "dark_gray", + "X c #BDB76B", "dark_khaki", + "X c #696969", "dim_gray", + "X c #B0C4DE", "light_steel_blue", + "X c #191970", "midnight_blue", + "X c #FFE4C4", "bisque", + "X c #6A5ACD", "slate_blue", + "X c #EE82EE", "violet", + "X c #8B4513", "saddle_brown", + "X c #FF7F50", "coral", + "X c #008000", "mid_green", + "X c #DB7093", "pale_violet_red", + "X c #C0C0C0", "silver", + "X c #E0FFFF", "light_cyan", + "X c #9370DB", "medium_purple", + "X c #FF8C00", "dark_orange", + "X c #00FA9A", "medium_spring_green", + "X c #E9967A", "dark_salmon", + "X c #778899", "light_slate_gray", + "X c #9932CC", "dark_orchid", + "X c #EEE8AA", "pale_goldenrod", + "X c #F8F8FF", "ghost_white", + "X c #FFA07A", "light_salmon", + "X c #ADD8E6", "light_blue", + "X c #D8BFD8", "thistle", + "X c #FFE4E1", "misty_rose", + "X c #FFDEAD", "navajo_white", + "X c #40E0D0", "turquoise", + "X c #90EE90", "light_green", + "X c #B22222", "fire_brick", + "X c #008080", "teal", + "X c #F0FFF0", "honeydew", + "X c #FFFACD", "lemon_chiffon", + "X c #FFF5EE", "seashell", + "X c #F5F5DC", "beige", + "X c #DCDCDC", "gainsboro", + "X c #FA8072", "salmon", + "X c #8B008B", "dark_magenta", + "X c #FFB6C1", "light_pink", + "X c #708090", "slate_gray", + "X c #87CEFA", "light_sky_blue", + "X c #FFEFD5", "papaya_whip", + "X c #D2B48C", "tan", + "X c #FFFFF0", "ivory", + "X c #F0FFFF", "azure", + "X c #F5DEB3", "wheat", + "X c #00008B", "dark_blue", + "X c #FFDAB9", "peach_puff", + "X c #8B0000", "dark_red", + "X c #FAF0E6", "linen", + "X c #B0E0E6", "powder_blue", + "X c #FFE4B5", "moccasin", + "X c #F5F5F5", "white_smoke", + "X c #FFF8DC", "cornsilk", + "X c #FFFAFA", "snow", + "X c #FFF0F5", "lavender_blush", + "X c #FFEBCD", "blanched_almond", + "X c #F0F8FF", "alice_blue", + "X c #FAEBD7", "antique_white", + "X c #FDF5E6", "old_lace", + "X c #FAFAD2", "light_goldenrod_yellow", + "X c #F5FFFA", "mint_cream", + "X c #FFFAF0", "floral_white", + "X c #7CFC00", "lawn_green" +}; + +static long seaColors[N_SEACOLORS]={0}; + +static void getRGB(long i, long &r, long &g, long &b) { + long code; + char *k; + + if (i >= 0x1000000 && i < 0x2000000) { + code = i - 0x1000000; + } else { + if (i < 0 || i >= N_SEACOLORS) { + r = 0; + g = 0; + b = 0; + return; + } + if (seaColors[i] == 0) { + k = strchr(coltab[i*2], '#'); + if (k) { + seaColors[i] = strtol(k+1, NULL, 16); + } + } + code = seaColors[i]; + } + b = code % 256; code = code / 256; + g = code % 256; code = code / 256; + r = code % 256; +} + +QPen thisPen(long i, int w) { + long r,g,b; + + getRGB(i, r, g, b); + if (w == 0) { + w = (3 * r + 5 * g + b) / 460; /* make pen larger for bright colors, 0 <= w <= 4 */ + if (w < 2) { + w = 2; + } + } + return QPen(QColor(r,g,b), w, Qt::SolidLine, Qt::FlatCap, Qt::BevelJoin); +}; + +static const char *miniPix[] = { + "1 1 1 1", + " c None", + " " +}; + + +// the following routine is for a color widget +const QPixmap *thisPixmap(long i, int w) { + char *penpix[3+11]; + long l, j, k; + + if (i < 0) { + return new QPixmap(miniPix); + } + if (i >= N_SEACOLORS) { + return NULL; + } + l = thisPen(i, w).width(); + if (l > 11) l = 11; + penpix[0] = "20 11 2 1"; + penpix[1] = coltab[i*2]; + penpix[2] = " c None"; + k = (11 - l) / 2; + for (j = 0; j < k; j++) { + penpix[j+3] = " "; + } + for (j = k; j < k + l; j++) { + penpix[j+3] = "XXXXXXXXXXXXXXXXXXXX"; + } + for (j = k + l; j < 11; j++) { + penpix[j+3] = " "; + } + return new QPixmap((const char **)penpix); +}; + +long Convert2ColorIndex(const QString &color) { + QString s=color.stripWhiteSpace(); + long rgb; + bool ok; + long r,g,b,rr,gg,bb,d; + long dist; + long index; + long i; + const char *ccol; + + if (s[0] == '#') { // hexadecimal color + s.remove(0,1); + rgb = s.toLong(&ok, 16); + if (!ok) { + return -2; + } + if (s.length() == 3) { // short form: find closest index + bb = rgb % 16 * 17; rgb = rgb / 16; + gg = rgb % 16 * 17; rgb = rgb / 16; + rr = rgb % 16 * 17; + dist = 9999999; + index = -2; + for (i = 0; i < N_SEACOLORS; i++) { + getRGB(i, r, g, b); + r -= rr; + g -= gg; + b -= bb; + d = 5*r*r + 8*g*g + 5*b*b; + if (d < dist) { + dist = d; + index = i; + } + } + } else if (s.length() == 6) { // long form: keep rgb code + index = 0x1000000 + rgb; + } else { + return -2; + } + return index; + } + index = s.toLong(&ok); + if (ok) { // color index + return index; + } + i = s.find("grey"); + if (i >= 0) { + s.replace(i+2, 1, "a"); + } + ccol = s.latin1(); + if (strcasecmp(ccol, "auto") == 0) { + return -1; + } + for (i = 0; i < N_SEACOLORS; i++) { + if (strcasecmp(coltab[i*2+1], ccol) == 0) { + return i; + } + } + if (strcasecmp(ccol, "green") == 0) { + return 2; + } + if (strcasecmp(ccol, "fuchsia") == 0) { + return 4; + } + if (strcasecmp(ccol, "aqua") == 0) { + return 6; + } + return -2; +} + +bool Convert2ColorName(long i, QString &result) { + if (i >= 0x1000000 && i < 0x2000000) { + result.sprintf("#%6.6lX", i - 0x1000000); + return true; + } + if (i < 0 || i >= N_SEACOLORS) { + result = "auto"; + return false; + } else { + result = coltab[i*2+1]; + return true; + } +} + +QString toRichText(QString &txt, bool *finished) { + QString result(""); + QChar ch; + int i, l; + bool wasspace; + + if (txt == "" || txt == " ") { + return " "; + } + l = txt.length(); + wasspace = true; + for (i = 0; i < l; i++) { + ch = txt[i]; + if (ch == ' ') { + if (wasspace) { + result.append(" "); + } else { + result.append(' '); + wasspace = true; + } + } else { + wasspace = false; + if (ch == '&') { + result.append("&"); + } else if (ch == '<') { + result.append("<"); + } else { + result.append(ch); + } + } + } + return result; +} + +MyTextEdit::MyTextEdit(QWidget * parent, const char *name) : QTextEdit(parent, name) +{ + QFont monospace("Courier"); + + setTextFormat(Qt::RichText); + setWordWrap(QTextEdit::WidgetWidth); + setWrapPolicy(QTextEdit::Anywhere); + monospace.setStyleHint(QFont::TypeWriter); + setFont(monospace); + linebuffer = ""; + buffered = false; + buffer = QStringList(); +} + +void MyTextEdit::appendHtml(QString text) { + linebuffer.append(text); +} + +void MyTextEdit::setBuffered(bool arg) { + if (buffered) { + hide(); + for (QStringList::Iterator it = buffer.begin(); it != buffer.end(); ++it ) { + append(*it); + } + show(); + buffer.clear(); + } + buffered = arg; +} + +void MyTextEdit::appendText(QString text) { + QChar ch; + int i, l; + bool wasspace; + + l = text.length(); + wasspace = true; + for (i = 0; i < l; i++) { + ch = text[i]; + if (ch == ' ') { + if (wasspace) { + linebuffer.append(" "); + } else { + linebuffer.append(' '); + wasspace = true; + } + } else { + wasspace = false; + if (ch == '\n') { + if (linebuffer == "" || linebuffer == " ") { + linebuffer="
"; + } + if (buffered) { + buffer.append(linebuffer); + } else { + append(linebuffer); + } + linebuffer = ""; + } else if (ch == '&') { + linebuffer.append("&"); + } else if (ch == '<') { + linebuffer.append("<"); + } else { + linebuffer.append(ch); + } + } + } +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..c6444db --- /dev/null +++ b/utils.h @@ -0,0 +1,59 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_SEACOLORS 138 + +class FlatButton: public QLabel { + Q_OBJECT +public: + FlatButton(const QString &label, QWidget *parent, const char *name=0); +signals: + void clicked(); + void mousePressed(); +protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); +private: + int x, y; +}; + +void ShrinkButton(QPushButton *b); +void SetEraseColor(QWidget *widget, QWidget *from, bool inclBgr = false); + +double DoubleTime(void); + +QPen thisPen(long i, int w = 0); + +const QPixmap *thisPixmap(long i, int w = 0); + +long Convert2ColorIndex(const QString &color); + +bool Convert2ColorName(long i, QString &result); + +// my own textedit, making paragraphs for newlines, but with single line spacing +class MyTextEdit: public QTextEdit { + Q_OBJECT +public: + MyTextEdit(QWidget * parent, const char *name=0); + void appendHtml(QString text); + void appendText(QString text); + void setBuffered(bool arg); +private: + bool buffered; + QString linebuffer; + QStringList buffer; +}; + + +#endif