diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmview | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmview')
92 files changed, 17715 insertions, 0 deletions
diff --git a/src/pmview/GNUmakefile b/src/pmview/GNUmakefile new file mode 100644 index 0000000..c6e039f --- /dev/null +++ b/src/pmview/GNUmakefile @@ -0,0 +1,102 @@ +TOPDIR = ../.. +COMMAND = pmview +PROJECT = $(COMMAND).pro +include $(TOPDIR)/src/include/builddefs + +WRAPPER = $(COMMAND).sh +QRCFILE = $(COMMAND).qrc +ICNFILE = $(COMMAND).icns +ICOFILE = $(COMMAND).ico +XMLFILE = $(COMMAND).info +DESKTOP = $(COMMAND).desktop +UIFILES = $(shell echo *.ui) +CLASSES = main.h pmview.h colorlist.h \ + barmod.h barobj.h baseobj.h \ + defaultobj.h gridobj.h labelobj.h stackobj.h \ + text.h viewobj.h pipeobj.h link.h xing.h \ + scenefileobj.h scenegroup.h \ + colorscalemod.h colormod.h colorscale.h \ + metriclist.h modlist.h modulate.h \ + scalemod.h stackmod.h togglemod.h \ + yscalemod.h pcpcolor.h launch.h +SOURCES = $(CLASSES:.h=.cpp) error.cpp +HEADERS = $(CLASSES) modobj.h +GENERATED = gram.cpp lex.cpp +LFILES = lex.l +YFILES = gram.y +LDIRT = $(COMMAND) $(WRAPPER) $(XMLFILE) $(GENERATED) gram.h y.tab.? images + +SUBDIRS = front-ends + +default: build-me + +include $(BUILDRULES) + +ifeq "$(ENABLE_QT)" "true" +build-me:: images wrappers $(GENERATED) + $(QTMAKE) + $(LNMAKE) + +build-me:: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +lex.cpp: lex.l + $(LEX) -t lex.l > $@ + +gram.h y.tab.c: gram.y + $(YACC) -d gram.y && cp y.tab.h gram.h + +gram.cpp: y.tab.c + cp y.tab.c $@ + +lex.o: gram.h + +ifeq ($(WINDOW),mac) +MACBUILD = $(COMMAND).app/Contents +PKG_MAC_DIR = /Applications/$(COMMAND).app/Contents +wrappers: $(WRAPPER) +else +wrappers: +endif + +$(WRAPPER): $(WRAPPER).IN + $(SED) -e '/\# .*/b' -e 's;PKG_MAC_DIR;$(PKG_MAC_DIR);g' < $< > $@ + +install: default + $(SUBDIRS_MAKERULE) + $(INSTALL) -m 755 -d $(PCP_BIN_DIR) +ifeq ($(WINDOW),win) + $(INSTALL) -m 755 $(BINARY) $(PKG_BIN_DIR)/$(COMMAND) +endif +ifeq ($(WINDOW),x11) + $(INSTALL) -m 755 $(BINARY) $(PKG_BIN_DIR)/$(COMMAND) + $(INSTALL) -m 755 -d $(PKG_DESKTOP_DIR) + $(INSTALL) -m 644 $(DESKTOP) $(PKG_DESKTOP_DIR)/$(DESKTOP) +endif +ifeq ($(WINDOW),mac) + $(INSTALL) -m 755 $(WRAPPER) $(PKG_BIN_DIR)/$(COMMAND) + $(INSTALL) -m 755 -d /Applications + $(INSTALL) -m 755 -d /Applications/$(COMMAND).app + $(INSTALL) -m 755 -d $(PKG_MAC_DIR) + $(INSTALL) -m 644 $(MACBUILD)/Info.plist $(PKG_MAC_DIR)/Info.plist + $(INSTALL) -m 644 $(MACBUILD)/PkgInfo $(PKG_MAC_DIR)/PkgInfo + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/MacOS + $(INSTALL) -m 755 $(BINARY) $(PKG_MAC_DIR)/MacOS/$(COMMAND) + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/Resources + $(INSTALL) -m 644 $(ICNFILE) $(PKG_MAC_DIR)/Resources/$(ICNFILE) +endif + +else +build-me: +install: +endif + +default_pcp: default + +install_pcp: install + +images: $(ICNFILE) + $(LN_S) $(TOPDIR)/images images + +$(ICNFILE): + $(LN_S) $(TOPDIR)/images/$(ICNFILE) $(ICNFILE) diff --git a/src/pmview/app-defaults b/src/pmview/app-defaults new file mode 100644 index 0000000..94cb915 --- /dev/null +++ b/src/pmview/app-defaults @@ -0,0 +1,207 @@ +! Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. +! +! This program is free software; you can redistribute it and/or modify it +! under the terms of the GNU General Public License as published by the +! Free Software Foundation; either version 2 of the License, or (at your +! option) any later version. +! +! This program is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +! for more details. +! +! You should have received a copy of the GNU General Public License along +! with this program; if not, write to the Free Software Foundation, Inc., +! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +! + + +! +!Activate schemes and sgi mode by default +! +PmView+*useSchemes: all +PmView+*sgiMode: true + +#ifdef __linux +! +! default fonts +! +PmView+*defaultFontList: -*-helvetica-bold-o-*-*-*-140-75-75-*-*-iso8859-1 +PmView+*form*fontList: -*-helvetica-medium-r-*-*-*-120-75-75-*-*-iso8859-1 +PmView+*form.scaleText.fontList: -*-helvetica-medium-r-*-*-*-140-75-75-*-*-iso8859-1 +PmView+*form.timeLabel.fontList: -*-helvetica-medium-r-*-*-*-140-75-75-*-*-iso8859-1 +PmView+*form.metricLabel.fontList: -*-helvetica-medium-r-*-*-*-140-75-75-*-*-iso8859-1 +#endif +! +!SGI Style guide specifies explicit focus within PmView+s +! +PmView+*keyboardFocusPolicy: explicit + +! +! Menus +! +PmView+*title: Performance Metrics Viewer +PmView+*fileMenu.labelString: File +PmView+*fileMenu.mnemonic: F +PmView+*recordButton.labelString: Record +PmView+*recordButton.mnemonic: R +PmView+*recordButton.accelerator: Ctrl<Key>R +PmView+*recordButton.acceleratorText: Ctrl+R +PmView+*saveButton.labelString: Save +PmView+*saveButton.mnemonic: S +PmView+*saveButton.accelerator: Ctrl<Key>S +PmView+*saveButton.acceleratorText: Ctrl+S +PmView+*printButton.labelString: Print +PmView+*printButton.mnemonic: P +PmView+*printButton.accelerator: Ctrl<Key>P +PmView+*printButton.acceleratorText: Ctrl+P +PmView+*exitButton.labelString: Quit +PmView+*exitButton.mnemonic: Q +PmView+*exitButton.accelerator: Ctrl<Key>Q +PmView+*exitButton.acceleratorText: Ctrl+Q +PmView+*optionsMenu.labelString: Options +PmView+*optionsMenu.mnemonic: O +PmView+*showVCRButton.labelString: Show Time Control +PmView+*showVCRButton.mnemonic: T +PmView+*showVCRButton.accelerator: Ctrl<Key>T +PmView+*showVCRButton.acceleratorText: Ctrl+T +PmView+*newVCRButton.mnemonic: N +PmView+*newVCRButton.labelString: New Time Control +PmView+*newVCRButton.accelerator: Ctrl<Key>N +PmView+*newVCRButton.acceleratorText: Ctrl+N + +PmView+*launchMenu.labelString: Launch +PmView+*launchMenu.mnemonic: L + +PmView+*helpPane.labelString: Help +PmView+*helpPane.mnemonic: H +PmView+*help_click_for_help.labelString: Click For Help +PmView+*help_click_for_help.mnemonic: C +PmView+*help_click_for_help.accelerator: Shift<Key>F1 +PmView+*help_click_for_help.acceleratorText: Shift+F1 +PmView+*help_overview.labelString: Overview +PmView+*help_overview.mnemonic: O +PmView+*help_index.labelString: Index +PmView+*help_index.mnemonic: I +PmView+*help_keys_and_short.labelString: Keys and Shortcuts +PmView+*help_keys_and_short.mnemonic: K +PmView+*help_prod_info.labelString: Product Information +PmView+*help_prod_info.mnemonic: P +PmView+*overviewButton.labelString: Overview +PmView+*overviewButton.mnemonic: O +PmView+*indexButton.labelString: Index +PmView+*indexButton.mnemonic: I + +! +! Scale Thumb Wheel (see SgThumbWheel(3)) +! These resources control the coarseness of the scale wheel (which is +! logarithmic). These settings force the wheel to have no maximum or +! minimum value and is reasonably fine near the center (scale of 1.0): +! +! PmView+*scaleWheel.homePosition: 0 +! PmView+*scaleWheel.maximum: 0 +! PmView+*scaleWheel.minimum: 0 +! PmView+*scaleWheel.unitsPerRotation: 100 +! +! These settings force a minimum and maximum value which is not as fine +! for values near the center: +! +! PmView+*scaleWheel.homePosition: 0 +! PmView+*scaleWheel.maximum: 80 +! PmView+*scaleWheel.minimum: -80 +! PmView+*scaleWheel.angleRange: 240 +! +PmView+*scaleText.value: 1.0000 +PmView+*scaleLabel.labelString: Scale +PmView+*scaleWheel.homePosition: 0 +PmView+*scaleWheel.maximum: 0 +PmView+*scaleWheel.minimum: 0 +PmView+*scaleWheel.unitsPerRotation: 100 +PmView+*scaleWheel.angleRange: 240 + +! +! Default label text +! +PmView+*metricLabel.labelString: \n +PMView*timeLabel.labelString: + +! +! Background color of read-only labels +! +!PmView+*readOnlyBackground: Black + +! +! Maximum value before saturation +! The default of 1.05 allows for 5% error in the time delta when +! determining rates, before values are deemed saturated. +! +PmView+*saturation: 1.05 + +! +! Use fast anti-aliasing +! See SoXtRenderArea(3) +! +PmView+*antiAliasSmooth: tree + +! +! Number of anti-aliasing passes: 1 to 255. Only 1 pass disables antialiasing. +PmView+*antiAliasPasses: 1 + +! +! Title, geometry etc. +! +PmView+*vkwindow.title: Performance Metrics Viewer +PmView+*vkwindow.geometry: 512x512 + +! +! Better handling of limited colors with pixmaps +! +PmView+*silenceWarnings: true +PmView+*xpmColorCloseness: 113512 + +! +! Dialogs +! +PmView+*fileSelectionDialog.width: 358 +PmView+*fileSelectionDialog.height: 417 + +! +! Help +! +*helpSubSys: pcp_eoe.books.PmViewHelp +*helpTitle: pmview Help + +! +! Layout - can be overridden in configuration files +! + +! Grid, Bar and Stack object base borders +PmView+*baseBorderWidth: 8 +PmView+*baseBorderDepth: 8 + +! Height of Grid, Bar and Stack bases +PmView+*baseHeight: 2 + +! Color of base plane +PmView+*baseColor: rgbi:0.15/0.15/0.15 + +! Spacing between Bar blocks +PmView+*barSpaceWidth: 8 +PmView+*barSpaceDepth: 8 + +! Spacing between Bar base and labels +PmView+*barSpaceLabel: 6 + +! Width and depth of Bar blocks +PmView+*barLength: 28 +PmView+*barHeight: 80 + +! Margin around a Label +PmView+*labelMargin: 5 + +! Color of labels +PmView+*labelColor: rgbi:1.0/1.0/1.0 + +! Width and depth of Grid columns and rows +PmView+*gridMinWidth: 20 +PmView+*gridMinDepth: 20 diff --git a/src/pmview/barmod.cpp b/src/pmview/barmod.cpp new file mode 100644 index 0000000..206f32c --- /dev/null +++ b/src/pmview/barmod.cpp @@ -0,0 +1,603 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/SoPath.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoSelection.h> +#include "barmod.h" +#include "modlist.h" +#include "launch.h" + +#include <iostream> +using namespace std; + +// +// Use debug flag LIBPMDA to trace Bar refreshes +// + +const char BarMod::theBarId = 'b'; + +BarMod::~BarMod() +{ +} + +BarMod::BarMod(MetricList *metrics, + SoNode *obj, + BarMod::Direction dir, + BarMod::Grouping group, + float xScale, float yScale, float zScale, + float xSpace, float zSpace) +: Modulate(metrics), + _blocks(), + _dir(dir), + _mod(BarMod::yScale), + _group(group), + _colScale(0.0, 0.0, 0.0), + _selectCount(0), + _infoValue(0), + _infoMetric(0), + _infoInst(0), + _xScale(xScale), + _yScale(yScale), + _zScale(zScale) +{ + generate(obj, xSpace, zSpace); +} + +BarMod::BarMod(MetricList *metrics, + const ColorScale &colScale, + SoNode *obj, + BarMod::Direction dir, + BarMod::Modulation mod, + BarMod::Grouping group, + float xScale, float yScale, float zScale, + float xSpace, float zSpace) +: Modulate(metrics), + _blocks(), + _dir(dir), + _mod(mod), + _group(group), + _colScale(colScale), + _selectCount(0), + _infoValue(0), + _infoMetric(0), + _infoInst(0), + _xScale(xScale), + _yScale(yScale), + _zScale(zScale) +{ + generate(obj, xSpace, zSpace); +} + +void +BarMod::generate(SoNode *obj, float xSpace, float zSpace) +{ + int numMetrics = _metrics->numMetrics(); + int numValues = _metrics->numValues(); + int maxInst = 0; + char buf[32]; + int m, i, v; + + _root = new SoSeparator; + + if (numValues > 0) { + + for (m = 0; m < numMetrics; m++) + if (_metrics->metric(m).numValues() > maxInst) + maxInst = _metrics->metric(m).numValues(); + + if (_dir == instPerCol) { + _cols = maxInst; + _rows = numMetrics; + } + else { + _cols = numMetrics; + _rows = maxInst; + } + + _blocks.resize(numValues); + + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + const QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + BarBlock &block = _blocks[v]; + sprintf(buf, "%c%d", theBarId, v); + block._sep = new SoSeparator; + block._sep->setName((SbName)buf); + _root->addChild(block._sep); + + block._tran = new SoTranslation; + block._sep->addChild(block._tran); + + block._color = new SoBaseColor; + block._sep->addChild(block._color); + + block._scale = new SoScale; + block._sep->addChild(block._scale); + + block._sep->addChild(obj); + } + } + + regenerate(_xScale, _zScale, xSpace, zSpace); + _infoValue = numValues; + + add(); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::generate: Added " << numValues << " in " << _cols + << " cols and " << _rows << " rows." << endl; +#endif + + } + + // Invalid object + else { + _sts = -1; + } +} + +void +BarMod::refresh(bool fetchFlag) +{ + int m, i, v; + + if (status() < 0) + return; + + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + + if (fetchFlag) + metric.update(); + + for (i = 0; i < metric.numValues(); i++, v++) { + + BarBlock &block = _blocks[v]; + + if (metric.error(i) <= 0) { + + if (block._state != Modulate::error) { + block._color->rgb.setValue(_errorColor.getValue()); + if (_mod != color) + block._scale->scaleFactor.setValue(_xScale, + theMinScale, + _zScale); + block._state = Modulate::error; + } + } + else { + double unscaled = metric.value(i); + double value = unscaled * theScale; + + if (value > theNormError) { + if (block._state != Modulate::saturated) { + block._color->rgb.setValue(Modulate::_saturatedColor); + if (_mod != color) + block._scale->scaleFactor.setValue(_xScale, + _yScale, + _zScale); + block._state = Modulate::saturated; + } + } + else { + if (block._state != Modulate::normal) { + block._state = Modulate::normal; + if (_mod == yScale) + block._color->rgb.setValue(_metrics->color(m).getValue()); + } + else if (_mod != yScale) + block._color->rgb.setValue(_colScale.step(unscaled).color().getValue()); + if (_mod != color) { + if (value < Modulate::theMinScale) + value = Modulate::theMinScale; + else if (value > 1.0) + value = 1.0; + block._scale->scaleFactor.setValue(_xScale, + _yScale * value, + _zScale); + } + + } + } + } + } +} + +void +BarMod::selectAll() +{ + int i; + + if (_selectCount == _blocks.size()) + return; + + theModList->selectAllId(_root, _blocks.size()); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::selectAll" << endl; +#endif + + for (i = 0; i < _blocks.size(); i++) { + if (_blocks[i]._selected == false) { + _selectCount++; + theModList->selectSingle(_blocks[i]._sep); + _blocks[i]._selected = true; + } + } +} + +int +BarMod::select(SoPath *path) +{ + int metric, inst, value; + + findBlock(path, metric, inst, value, false); + if (value < _blocks.size() && _blocks[value]._selected == false) { + _blocks[value]._selected = true; + _selectCount++; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::select: value = " << value + << ", count = " << _selectCount << endl; +#endif + } + return _selectCount; +} + +int +BarMod::remove(SoPath *path) +{ + int metric, inst, value; + + findBlock(path, metric, inst, value, false); + if (value < _blocks.size() && _blocks[value]._selected == true) { + _blocks[value]._selected = false; + _selectCount--; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::remove: value = " << value + << ", count = " << _selectCount << endl; +#endif + + } + +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::remove: did not remove " << value + << ", count = " << _selectCount << endl; +#endif + + return _selectCount; +} + +void BarMod::infoText(QString &str, bool selected) const +{ + int m = _infoMetric; + int i = _infoInst; + int v = _infoValue; + bool found = false; + + if (selected && _selectCount == 1) { + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + const QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) + if (_blocks[v]._selected) { + found = true; + break; + } + if (found) + break; + } + } + + if (v >= _blocks.size()) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::infoText: infoText requested but nothing selected" + << endl; +#endif + str = ""; + } + else { + const QmcMetric &metric = _metrics->metric(m); + str = metric.spec(true, true, i); + str.append(QChar('\n')); + + if (_blocks[v]._state == Modulate::error) + str.append(theErrorText); + else if (_blocks[v]._state == Modulate::start) + str.append(theStartText); + else { + QString value; + str.append(value.setNum(metric.realValue(i), 'g', 4)); + str.append(QChar(' ')); + if (metric.desc().units().size() > 0) + str.append(metric.desc().units()); + str.append(" ["); + str.append(value.setNum(metric.value(i) * 100.0, 'g', 4)); + str.append("% of expected max]"); + } + } +} + +void BarMod::launch(Launch &launch, bool all) const +{ + int m, i, v; + bool needClose; + bool always = all; + bool keepGoing = true; + + if (status() < 0) + return; + + if (_selectCount == _blocks.size()) + always = true; + + // Group by metric + if (_group == groupByMetric || + (_group == groupByRow && _dir == instPerCol) || + (_group == groupByCol && _dir == instPerRow)) { + + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + + // Do we have to check that an instance of this metric has + // been selected? + if (!always) { + needClose = false; + for (i = 0; i < metric.numValues(); i++, v++) { + if (_blocks[v]._selected) { + if (needClose == false) { + launch.startGroup("point"); + needClose = true; + } + if (_mod == yScale) + launch.addMetric(metric, _metrics->color(m), i); + else + launch.addMetric(metric, _colScale, i); + } + } + if (needClose) + launch.endGroup(); + } + else { + launch.startGroup("point"); + for (i = 0; i < metric.numValues(); i++, v++) { + if (_mod == yScale) + launch.addMetric(metric, _metrics->color(m), i); + else + launch.addMetric(metric, _colScale, i); + } + launch.endGroup(); + } + } + } + + // Group by instance, this gets a little tricky + else { + for (i = 0; keepGoing ; i++) { + needClose = false; + keepGoing = false; + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + if (metric.numValues() > i) { + if (always || _blocks[v+i]._selected) { + if (needClose == false) { + launch.startGroup("point"); + needClose = true; + } + if (_mod == yScale) + launch.addMetric(metric, _metrics->color(m), i); + else + launch.addMetric(metric, _colScale, i); + } + keepGoing = true; + } + v += metric.numValues(); + } + if (needClose) + launch.endGroup(); + } + } +} + +void +BarMod::selectInfo(SoPath *path) +{ + findBlock(path, _infoMetric, _infoInst, _infoValue); +} + +void +BarMod::removeInfo(SoPath *) +{ + _infoValue = _blocks.size(); + _infoMetric = _infoInst = 0; +} + +void +BarMod::dump(QTextStream &os) const +{ + int m, i, v; + + os << "BarMod: "; + + if (_dir == instPerCol) + os << "inst per col"; + else + os << "inst per row"; + + if (_mod == yScale) + os << ", Y-Scale: "; + else if (_mod == color) + os << ", Color Only: "; + else + os << ", Color & Y-Scale: "; + + if (status() < 0) + os << "Invalid metrics: " << pmErrStr(status()) << endl; + else { + os << endl; + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + os << " [" << v << "]: "; + if (_blocks[v]._selected == true) + os << '*'; + else + os << ' '; + dumpState(os, _blocks[v]._state); + os << ": "; + metric.dump(os, true, i); + } + } + } +} + +void +BarMod::findBlock(SoPath *path, int &metric, int &inst, + int &value, bool idMetric) +{ + SoNode *node; + char *str; + int m, i, v; + char c; + + for (i = path->getLength() - 1; i >= 0; --i) { + node = path->getNode(i); + str = (char *)(node->getName().getString()); + if (strlen(str) && str[0] == theBarId) + break; + } + + if (i >= 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "BarMod::findBlock: Bar id = " << str << endl; +#endif + + sscanf(str, "%c%d", &c, &value); + + if (value == 0 || idMetric == false) { + metric = 0; + inst = 0; + } + else { + m = 0; + v = value; + while (m < _metrics->numMetrics()) { + i = _metrics->metric(m).numValues(); + if (v < i) { + metric = m; + inst = v; + break; + } + else { + v -= i; + m++; + } + } + } + } + else { + value = _blocks.size(); + metric = inst = 0; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + cerr << "BarMod::findBlock: metric = " << metric + << ", inst = " << inst << ", value = " << value << endl; + } +#endif + + return; +} + +void +BarMod::regenerate(float xScale, float zScale, float xSpace, float zSpace) +{ + int m, i, v; + float halfX = xScale / 2.0; + float halfZ = zScale / 2.0; + + if (status() < 0) + return; + + _xScale = xScale; + _zScale = zScale; + + _width = (unsigned int)((_cols * (_xScale + xSpace)) - xSpace); + _depth = (unsigned int)((_rows * (_zScale + zSpace)) - zSpace); + + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + const QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + BarBlock &block = _blocks[v]; + + if (_dir == instPerCol) + block._tran->translation.setValue(i * (_xScale+xSpace) + halfX, + 0, + m * (_zScale+zSpace) + halfZ); + else + block._tran->translation.setValue(m * (_xScale+xSpace) + halfX, + 0, + i * (_zScale+zSpace) + halfZ); + + block._color->rgb.setValue(_errorColor.getValue()); + block._scale->scaleFactor.setValue(_xScale, _yScale, _zScale); + block._state = Modulate::start; + block._selected = false; + } + } +} + +const char * +BarMod::dirStr() const +{ + const char *str = NULL; + + if (_dir == instPerCol) + str = "instances in columns"; + else + str = "instances in rows"; + + return str; +} + +const char * +BarMod::modStr() const +{ + const char *str = NULL; + + switch (_mod) { + case yScale: + str = "Y-Scale"; + break; + case color: + str = "Colored"; + break; + case colYScale: + str = "Colored Y-Scale"; + break; + } + return str; +} diff --git a/src/pmview/barmod.h b/src/pmview/barmod.h new file mode 100644 index 0000000..073f7ce --- /dev/null +++ b/src/pmview/barmod.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _BARMOD_H_ +#define _BARMOD_H_ + +#include "colorscale.h" +#include "modulate.h" +#include <QtCore/QVector> + +class SoBaseColor; +class SoScale; +class SoNode; +class SoTranslation; +class Launch; + +struct BarBlock { + SoSeparator *_sep; + SoBaseColor *_color; + SoScale *_scale; + SoTranslation *_tran; + Modulate::State _state; + bool _selected; +}; + +typedef QVector<BarBlock> BarBlockList; + +class BarMod : public Modulate +{ +public: + + enum Direction { instPerCol, instPerRow }; + enum Modulation { yScale, color, colYScale }; + enum Grouping { groupByRow, groupByCol, groupByMetric, groupByInst }; + +private: + + static const char theBarId; + + BarBlockList _blocks; + Direction _dir; + Modulation _mod; + Grouping _group; + ColorScale _colScale; + int _selectCount; + int _infoValue; + int _infoMetric; + int _infoInst; + float _xScale; + float _yScale; + float _zScale; + int _width; + int _depth; + int _cols; + int _rows; + +public: + + virtual ~BarMod(); + + BarMod(MetricList *list, + SoNode *obj, + BarMod::Direction dir, + BarMod::Grouping group, + float xScale, float yScale, float zScale, + float xSpace, float zSpace); + + BarMod(MetricList *list, + const ColorScale &colScale, + SoNode *obj, + BarMod::Direction dir, + BarMod::Modulation mod, + BarMod::Grouping group, + float xScale, float yScale, float zScale, + float xSpace, float zSpace); + + Direction dir() const + { return _dir; } + int width() const + { return _width; } + int depth() const + { return _depth; } + int numBars() const + { return _blocks.size(); } + int rows() const + { return _rows; } + int cols() const + { return _cols; } + + virtual void refresh(bool fetchFlag); + + virtual void selectAll(); + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void selectInfo(SoPath *); + virtual void removeInfo(SoPath *); + + virtual void infoText(QString &str, bool) const; + + virtual void launch(Launch &launch, bool) const; + + virtual void dump(QTextStream &) const; + + void regenerate(float xScale, float zScale, float xSpace, float zSpace); + + const char *dirStr() const; + const char *modStr() const; + +private: + + BarMod(); + BarMod(const BarMod &); + const BarMod &operator=(const BarMod &); + // Never defined + + void generate(SoNode *obj, float xSpace, float zSpace); + void findBlock(SoPath *path, int &metric, int &inst, + int &value, bool idMetric = true); +}; + +#endif /* _BARMOD_H_ */ diff --git a/src/pmview/barobj.cpp b/src/pmview/barobj.cpp new file mode 100644 index 0000000..da3e621 --- /dev/null +++ b/src/pmview/barobj.cpp @@ -0,0 +1,539 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoTransform.h> + +#include "barobj.h" +#include "colorlist.h" +#include "yscalemod.h" +#include "colormod.h" +#include "colorscalemod.h" +#include "text.h" +#include "defaultobj.h" + +#include <iostream> +using namespace std; + +BarObj::~BarObj() +{ +} + +BarObj::BarObj(ViewObj::Shape shape, + BarMod::Direction dir, + BarMod::Modulation mod, + BarMod::Grouping group, + bool baseFlag, + const DefaultObj &defaults, + int x, int y, + int cols, int rows, + BaseObj::Alignment align) +: ModObj(baseFlag, defaults, x, y, cols, rows, align), + _shape(shape), + _dir(dir), + _mod(mod), + _group(group), + _width(0), + _depth(0), + _xSpace(defaults.barSpaceX()), + _zSpace(defaults.barSpaceZ()), + _labelSpace(defaults.barSpaceLabel()), + _bars(0), + _metDir(towards), + _metLabels(new QStringList), + _instDir(away), + _instLabels(new QStringList) +{ + _objtype |= BAROBJ; + + int i; + for (i = 0; i < numSides; i++) + _margins[i] = 0.0; + _labelColor[0] = defaults.labelColor(0); + _labelColor[1] = defaults.labelColor(1); + _labelColor[2] = defaults.labelColor(2); +} + +void +BarObj::finishedAdd() +{ + const ColorSpec *colSpec = NULL; + SoNode *object = ViewObj::object(_shape); + SoSeparator *labelSep = NULL; + SoSeparator *metricSep = NULL; + SoSeparator *instSep = NULL; + SoSeparator *barSep = new SoSeparator; + SoSeparator *baseSep = new SoSeparator; + SoTranslation *objTran = new SoTranslation; + SoTranslation *barTran = new SoTranslation; + SoTranslation *baseTran = new SoTranslation; + SoTranslation *modTran = new SoTranslation; + ColorScale *colScale = NULL; + LabelSide metSide = left; + LabelSide instSide = left; + Text **metText = NULL; + Text **instText = NULL; + int i; + int max = 0; + int numMetLabels = 0; + int numInstLabels = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "BarObj::finishedAdd:" << endl; +#endif + + if (_metrics.numMetrics() == 0) { + BaseObj::addBase(_root); + pmprintf("%s: Error: Bar object has no metrics\n", + pmProgname); + _length = 0; + _width = baseWidth(); + _depth = baseDepth(); + return; + } + + _root->addChild(objTran); + + if (_metLabels->size() || _instLabels->size()) { + labelSep = new SoSeparator; + _root->addChild(labelSep); + SoBaseColor *base = new SoBaseColor; + base->rgb.setValue(_labelColor[0], _labelColor[1], _labelColor[2]); + labelSep->addChild(base); + } + _root->addChild(barSep); + barSep->addChild(barTran); + barSep->addChild(baseSep); + baseSep->addChild(baseTran); + barSep->addChild(modTran); + BaseObj::addBase(baseSep); + + // Determine color mapping + + if (_colors.size()) + colSpec = theColorLists.list((const char *)_colors.toAscii()); + + if (colSpec != NULL) { + if (colSpec->_scale) { + if (_mod == BarMod::yScale) { + pmprintf("%s: Warning: Color scale ignored for Y-Scale Bar object.\n", + pmProgname); + } + else { + if (colSpec->_list.size() == 0) + colScale = new ColorScale(0.0, 0.0, 1.0); + else { + colScale = new ColorScale(*(colSpec->_list[0])); + for (i = 1; i < colSpec->_list.size(); i++) + colScale->add(new ColorStep(*(colSpec->_list[i]), + colSpec->_max[i])); + } + } + } + else if (_mod == BarMod::color || _mod == BarMod::colYScale) { + pmprintf("%s: Warning: Expected color scale for color modulated Bar object.\n", + pmProgname); + + if (colSpec->_list.size() == 0) + colScale = new ColorScale(0.0, 0.0, 1.0); + else + colScale = new ColorScale(*(colSpec->_list[0])); + } + } + else { + pmprintf("%s: Warning: No colours specified for Bar objects, defaulting to blue.\n", + pmProgname); + + if (_mod == BarMod::color || _mod == BarMod::colYScale) + colScale = new ColorScale(0.0, 0.0, 1.0); + } + + if (_mod == BarMod::yScale) { + if (colSpec != NULL) + for (i = 0; i < colSpec->_list.size(); i++) + _metrics.add(*(colSpec->_list)[i]); + _metrics.resolveColors(MetricList::perMetric); + } + + // Generate Bar Modulate Object + if (_mod == BarMod::yScale) + _bars = new BarMod(&_metrics, object, _dir, _group, + (float)_length, (float)_maxHeight, (float)_length, + (float)_xSpace, (float)_zSpace); + else { + _bars = new BarMod(&_metrics, *colScale, object, _dir, _mod, _group, + (float)_length, (float)_maxHeight, (float)_length, + (float)_xSpace, (float)_zSpace); + } + + barSep->addChild(_bars->root()); + BaseObj::add(_bars); + + // Generate Labels + + if (_metLabels->size()) { + if (_dir == BarMod::instPerRow) + if (_metDir == away) + metSide = below; + else + metSide = above; + else + if (_metDir == away) + metSide = right; + else + metSide = left; + + metricSep = new SoSeparator; + labelSep->addChild(metricSep); + + if (_metLabels->size() < _metrics.numMetrics()) + numMetLabels = _metLabels->size(); + else + numMetLabels = _metrics.numMetrics(); + + metText = calcLabels(*_metLabels, metSide, numMetLabels); + } + + if (_instLabels->size()) { + if (_dir == BarMod::instPerCol) { + max = _bars->cols(); + if (_instDir == away) + instSide = below; + else + instSide = above; + } + else { + max = _bars->rows(); + if (_instDir == away) + instSide = right; + else + instSide = left; + } + + instSep = new SoSeparator; + labelSep->addChild(instSep); + + if (_instLabels->size() < max) + numInstLabels = _instLabels->size(); + else + numInstLabels = max; + + instText = calcLabels(*_instLabels, instSide, numInstLabels); + } + + // Width and depth of bars only, effects of labels added later + + _width = _bars->width(); + _depth = _bars->depth(); + + // Insert the labels + + if (numMetLabels) + metricSep->addChild(doLabels(metText, metSide, numMetLabels)); + + if (numInstLabels) + instSep->addChild(doLabels(instText, instSide, numInstLabels)); + + // Work out where the bars live + + _bars->regenerate(_length, _length, _xSpace, _zSpace); + _width = _bars->width(); + _depth = _bars->depth(); + + baseTran->translation.setValue(_width / 2.0, 0.0, _depth / 2.0); + + _width += (u_int32_t)(baseWidth() + _margins[left] + _margins[right]+0.5); + _depth += (u_int32_t)(baseDepth() + _margins[above] + _margins[below]+0.5); + + objTran->translation.setValue((_width / -2.0), 0.0, (_depth / -2.0)); + + barTran->translation.setValue(_margins[left] + borderX(), 0.0, + _margins[above] + borderZ()); + + + modTran->translation.setValue(0.0, + (BaseObj::state() ? baseHeight() : 0.0), + 0.0); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "BarObj::finishedAdd: metric list = " << endl + << _metrics << endl; +#endif + + if (_metrics.numMetrics()) + ViewObj::theNumModObjects++; + + // Cleanup + + if (colScale) + delete colScale; + delete _metLabels; + delete _instLabels; +} + +void +BarObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + BaseObj::setBaseSize(width() - _margins[left] - _margins[right], + depth() - _margins[above] - _margins[below]); + BaseObj::setTran(xTran + (width() / 2.0), + zTran + (depth() / 2.0), + setWidth, setDepth); +} + +QTextStream& +operator<<(QTextStream& os, BarObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +BarObj::display(QTextStream& os) const +{ + BaseObj::display(os); + + if (_bars == NULL) { + os << "No valid metrics" << endl; + return; + } + + os << ", dir = " + << (_dir == BarMod::instPerCol ? "instPerCol" : "instPerRow") + << ", length = " << _length << ", xSpace = " << _xSpace << ", zSpace = " + << _zSpace << ", labelSpace = " << _labelSpace << ", rows = " << _rows + << ", cols = " << _cols << ", num bars = " << _bars->numBars() + << ", shape = "; + ViewObj::dumpShape(os, _shape); + os << ", margins: left = " << _margins[left] << ", right = " + << _margins[right] << ", above = " << _margins[above] + << ", below = " << _margins[below]; +} + +const char* +BarObj::name() const +{ + static QString myName; + + if (myName.size() == 0) { + if (_bars == NULL) + myName = "Invalid bar object"; + else { + myName = _bars->modStr(); + myName.append(" Bar Object ("); + myName.append(_bars->dirStr()); + myName.append(QChar(')')); + } + } + + return (const char *)myName.toAscii(); +} + +Text ** +BarObj::calcLabels(const QStringList &labels, LabelSide side, int numLabels) +{ + Text **text = NULL; + int i; + int maxWidth = 0; + int maxDepth = 0; + + text= new Text*[numLabels]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + cerr << "BarObj::calcLabels: " << numLabels << " labels on the "; + switch(side) { + case left: + cerr << "left"; + break; + case right: + cerr << "right"; + break; + case above: + cerr << "above"; + break; + case below: + cerr << "below"; + break; + } + cerr << " side" << endl; + } +#endif + + // Create the text objects so that we know how big they are + + for (i = 0; i < numLabels; i++) { + if (side == above || side == below) + text[i] = new Text(labels[i], Text::down, Text::medium); + else + text[i] = new Text(labels[i], Text::right, Text::medium); + + if (text[i]->width() > maxWidth) + maxWidth = text[i]->width(); + if (text[i]->depth() > maxDepth) + maxDepth = text[i]->depth(); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "BarObj::calcLabels: maxWidth = " << maxWidth + << ", maxDepth = " << maxDepth << endl; +#endif + + // Determine if the size of the bars will need to be increased + + if (side == above || side == below) { + _margins[side] = maxDepth + _labelSpace; + if (maxWidth > _length) { + _length = maxWidth; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "BarObj::calcLabels: length (width) increased to " + << _length << endl; +#endif + + } + } + else { + _margins[side] = maxWidth + _labelSpace; + if (maxDepth > _length) { + _length = maxDepth; + + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "BarObj::calcLabels: length (depth) increased to " + << _length << endl; +#endif + } + } + return text; +} + +SoNode * +BarObj::doLabels(Text **text, LabelSide side, int numLabels) +{ + SoSeparator *sep = new SoSeparator; + SoTranslation *tran = new SoTranslation; + int i; + int maxWidth = 0; + int maxDepth = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + cerr << "BarObj::doLabels: " << numLabels << " labels on the "; + switch(side) { + case left: + cerr << "left"; + break; + case right: + cerr << "right"; + break; + case above: + cerr << "above"; + break; + case below: + cerr << "below"; + break; + } + cerr << " side" << endl; + } +#endif + + sep->addChild(tran); + + // Determine the translation to the first label, subsequent labels + // are translated from the first + + maxWidth = _length + _xSpace; + maxDepth = _length + _zSpace; + + switch (side) { + case left: + tran->translation.setValue(_margins[left] - _labelSpace, 0.0, + _margins[above] + borderZ()); + break; + case right: + tran->translation.setValue( + _margins[left] + _width + baseWidth() + _labelSpace, + 0.0, _margins[above] + borderZ()); + break; + case above: + tran->translation.setValue(_margins[left] + borderX(), 0.0, + _margins[above] - _labelSpace); + break; + case below: + tran->translation.setValue(_margins[left] + borderX(), 0.0, + _margins[above] + _depth + baseDepth() + _labelSpace); + break; + default: + break; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + float x, y, z; + tran->translation.getValue().getValue(x, y, z); + cerr << "BarObj::doLabels: translation set to " << x << ',' << y + << ',' << z << endl; + } +#endif + + // Add each label to the scene graph + + for (i = 0; i < numLabels; i++) { + SoSeparator *labelSep = new SoSeparator; + sep->addChild(labelSep); + + SoTranslation *labelTran = new SoTranslation; + labelSep->addChild(labelTran); + + switch (side) { + case left: + labelTran->translation.setValue(0.0, + 0.0, + (maxDepth * i) + ((_length - (float)text[i]->depth())/ 2.0)); + break; + case right: + labelTran->translation.setValue(text[i]->width(), 0.0, + (maxDepth * i) + ((_length - (float)text[i]->depth())/ 2.0)); + break; + case above: + labelTran->translation.setValue( + (maxWidth * i) + ((_length - (float)text[i]->width())/ 2.0), + 0.0, 0.0); + break; + case below: + labelTran->translation.setValue( + (maxWidth * i) + ((_length - (float)text[i]->width())/ 2.0), + 0.0, text[i]->depth()); + break; + default: + break; + } + + labelSep->addChild(text[i]->root()); + } + + // Do not delete contents, just the array pointer + delete [] text; + + return sep; +} diff --git a/src/pmview/barobj.h b/src/pmview/barobj.h new file mode 100644 index 0000000..c9566a7 --- /dev/null +++ b/src/pmview/barobj.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _BAROBJ_H_ +#define _BAROBJ_H_ + +#include "barmod.h" +#include "modobj.h" +#include "metriclist.h" +#include <QtCore/QStringList> + +class SoNode; +class SoTranslation; +class Text; + +class BarObj : public ModObj +{ +public: + + enum LabelDir { away, towards }; + enum LabelSide { left, right, above, below, numSides }; + +protected: + + ViewObj::Shape _shape; + BarMod::Direction _dir; + BarMod::Modulation _mod; + BarMod::Grouping _group; + int _width; + int _depth; + int _xSpace; + int _zSpace; + int _labelSpace; + BarMod *_bars; + LabelDir _metDir; + QStringList *_metLabels; + LabelDir _instDir; + QStringList *_instLabels; + float _margins[numSides]; + float _labelColor[3]; + +public: + + virtual ~BarObj(); + + BarObj(ViewObj::Shape shape, + BarMod::Direction dir, + BarMod::Modulation mod, + BarMod::Grouping group, + bool baseFlag, + const DefaultObj &defaults, + int x, int y, + int cols = 1, int rows = 1, + BaseObj::Alignment align = BaseObj::center); + + virtual int width() const + { return _width; } + virtual int depth() const + { return _depth; } + Shape shape() const + { return _shape; } + BarMod::Direction dir() const + { return _dir; } + BarMod::Modulation mod() const + { return _mod; } + int numMetricLabels() const + { return _metLabels->size(); } + LabelDir metricLabelDir() const + { return _metDir; } + int numInstLabels() const + { return _instLabels->size(); } + LabelDir instLabelDir() const + { return _instDir; } + + void addMetric(const char *metric, double scale, const char *label) + { if (_metrics.add(metric, scale) >= 0) _metLabels->append(label); } + + void addMetricLabel(const char *label) + { _metLabels->append(label); } + void addInstLabel(const char *label) + { _instLabels->append(label); } + + virtual void finishedAdd(); + + // Local change + int &xSpace() + { return _xSpace; } + int &zSpace() + { return _zSpace; } + LabelDir &metricLabelDir() + { return _metDir; } + LabelDir &instLabelDir() + { return _instDir; } + + virtual void setTran(float xTran, float zTran, int width, int depth); + + virtual const char* name() const; + + virtual void display(QTextStream& os) const; + + friend QTextStream& operator<<(QTextStream& os, BarObj const& rhs); + +private: + + Text ** calcLabels(const QStringList &labels, LabelSide side, + int numLabels); + SoNode *doLabels(Text **text, LabelSide side, int numLabels); + + BarObj(); + BarObj(BarObj const&); + BarObj const& operator=(BarObj const &); +}; + +#endif /* _BAROBJ_H_ */ diff --git a/src/pmview/baseobj.cpp b/src/pmview/baseobj.cpp new file mode 100644 index 0000000..8646381 --- /dev/null +++ b/src/pmview/baseobj.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoCube.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include "baseobj.h" +#include "defaultobj.h" + +BaseObj::~BaseObj() +{ + delete(_mod); +} + +BaseObj::BaseObj(bool onFlag, + const DefaultObj &defaults, + int x, int y, + int cols, int rows, + BaseObj::Alignment align) +: ViewObj(x, y, cols, rows, align), + _on(onFlag), + _borderX(defaults.baseBorderX()), + _borderZ(defaults.baseBorderZ()), + _baseHeight(defaults.baseHeight()), + _length(defaults.barLength()), + _maxHeight(defaults.barHeight()), + _mod(0), + _cube(0), + _label("\n") +{ + _objtype |= BASEOBJ; + + _baseColor[0] = defaults.baseColor(0); + _baseColor[1] = defaults.baseColor(1); + _baseColor[2] = defaults.baseColor(2); +} + +void +BaseObj::addBase(SoSeparator *sep) +{ + SoSeparator *cubeSep = new SoSeparator; + + if (_on) { + SoTranslation *tran = new SoTranslation; + tran->translation.setValue(0.0, _baseHeight / 2, 0.0); + sep->addChild(tran); + SoBaseColor *col = new SoBaseColor; + col->rgb.setValue(_baseColor[0], _baseColor[1], _baseColor[2]); + cubeSep->addChild(col); + _cube = new SoCube; + _cube->width = baseWidth(); + _cube->depth = baseDepth(); + _cube->height = _baseHeight; + cubeSep->addChild(_cube); + } + + _mod = new ToggleMod(cubeSep, (const char *)_label.toAscii()); + sep->addChild(_mod->root()); + + if (_on) { + SoTranslation *tran2 = new SoTranslation; + tran2->translation.setValue(0.0, _baseHeight / 2.0, 0.0); + sep->addChild(tran2); + } +} + +void +BaseObj::setBaseSize(int width, int depth) +{ + if (_on) { + _cube->width = width; + _cube->depth = depth; + } +} + +#if 0 +void +BaseObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + ViewObj::setTran(xTran, zTran, setWidth, setDepth); +} + +#endif + +QTextStream& +operator<<(QTextStream& os, BaseObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +BaseObj::display(QTextStream& os) const +{ + ViewObj::display(os); + os << ", border: X = " << _borderX << ", Z = " << _borderZ + << ", height = " << _baseHeight << ", length = " << _length + << ", maxHeight = " << _maxHeight << ", color = " << _baseColor[0] + << ',' << _baseColor[1] << ',' << _baseColor[2] << ", on = " + << (_on == true ? "true" : "false") << ", label = " + << (_label == "\n" ? "\\n" : _label); +} + +void +BaseObj::add(Modulate *mod) +{ + _mod->addMod(mod); +} + +void +BaseObj::finishedAdd() +{ +} diff --git a/src/pmview/baseobj.h b/src/pmview/baseobj.h new file mode 100644 index 0000000..e2736c0 --- /dev/null +++ b/src/pmview/baseobj.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _BASEOBJ_H_ +#define _BASEOBJ_H_ + +#include "viewobj.h" +#include "togglemod.h" + +class SoCube; + +class BaseObj : public ViewObj +{ +protected: + + bool _on; + int _borderX; + int _borderZ; + int _baseHeight; + int _length; + int _maxHeight; + float _baseColor[3]; + ToggleMod *_mod; + SoCube *_cube; + QString _label; + +public: + + virtual ~BaseObj(); + + BaseObj(bool onFlag, + const DefaultObj &defaults, + int, int, + int cols = 1, int rows = 1, + BaseObj::Alignment align = BaseObj::center); + + int borderX() const + { return _borderX; } + int borderZ() const + { return _borderZ; } + int baseWidth() const + { return _borderX * 2; } + int baseDepth() const + { return _borderZ * 2; } + int baseHeight() const + { return _baseHeight; } + float baseColor(int i) const + { return _baseColor[i]; } + const QString &label() const + { return _label; } + int length() const + { return _length; } + int maxHeight() const + { return _maxHeight; } + + // Local changes + int &borderX() + { return _borderX; } + int &borderZ() + { return _borderZ; } + int &baseHeight() + { return _baseHeight; } + void baseColor(float r, float g, float b) + { _baseColor[0] = r; _baseColor[1] = g; _baseColor[2] = b; } + QString &label() + { return _label; } + int &length() + { return _length; } + int& maxHeight() + { return _maxHeight; } + + bool state() const + { return _on; } + void state(bool flag); + + virtual int width() const = 0; + virtual int depth() const = 0; + + void addBase(SoSeparator *sep); + + void setBaseSize (int width, int depth); + +// virtual void setTran(float xTran, float zTran, int width, int depth); + + virtual Modulate *modObj() + { return _mod; } + + virtual void finishedAdd(); + + // Output + virtual void display(QTextStream& os) const; + + virtual const char* name() const = 0; + + friend QTextStream& operator<<(QTextStream& os, BaseObj const& rhs); + +protected: + + void add(Modulate *mod); + +private: + + BaseObj(); + BaseObj(BaseObj const &); + BaseObj const& operator=(BaseObj const &); +}; + +#endif /* _BASEOBJ_H_ */ diff --git a/src/pmview/colorlist.cpp b/src/pmview/colorlist.cpp new file mode 100644 index 0000000..5709a89 --- /dev/null +++ b/src/pmview/colorlist.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "main.h" +#include "colorlist.h" +#include <QtGui/QColor> + +ColorList theColorLists; + +ColorList::~ColorList() +{ + int i, j; + + for (i = 0; i < _colors.size(); i++) { + ColorSpec &spec = *_colors[i]; + for (j = 0; j < spec._list.size(); j++) + delete spec._list[j]; + delete _colors[i]; + } +} + +ColorList::ColorList() +: _names(), + _colors() +{ +} + +const ColorSpec * +ColorList::list(const char *name) +{ + int i; + + for (i = 0; i < _names.size(); i++) + if (_names[i] == name) + return _colors[i]; + return NULL; +} + +bool +ColorList::add(const char *name, const char *scaleColor) +{ + if (list(name) != NULL) + return false; + + _names.append(name); + if (scaleColor != NULL) { + _colors.append(new ColorSpec(true)); + if (addColor(scaleColor, 0.0) == false) { + _colors.last()->_list.append(new SbColor(0.0, 0.0, 1.0)); + _colors.last()->_max.append(0.0); + } + } + else + _colors.append(new ColorSpec(false)); + + return true; +} + +bool +ColorList::add(const char *name, float red, float green, float blue) +{ + if (list(name) != NULL) + return false; + + _names.append(name); + _colors.append(new ColorSpec(true)); + _colors.last()->_list.append(new SbColor(red, green, blue)); + _colors.last()->_max.append(0.0); + + return true; +} + +bool +ColorList::findColor(const char *color, float &red, float &green, float &blue) +{ + QColor col; + + col.setAllowX11ColorNames(true); + col.setNamedColor(color); + if (!col.isValid()) + return false; + + red = col.redF(); + green = col.greenF(); + blue = col.blueF(); + return true; +} + +bool +ColorList::findColor(const char *color) +{ + QColor col; + + col.setAllowX11ColorNames(true); + col.setNamedColor(color); + if (!col.isValid()) + return false; + + _colors.last()->_list.append( + new SbColor(col.redF(), col.greenF(), col.blueF())); + return true; +} + +bool +ColorList::addColor(const char *color) +{ + assert(_colors.size() > 0); + assert(_colors.last()->_scale == false); + return findColor(color); +} + +bool +ColorList::addColor(const char *color, double max) +{ + bool result; + + assert(_colors.size() > 0); + assert(_colors.last()->_scale == true); + result = findColor(color); + if (result) + _colors.last()->_max.append(max); + return result; +} + +bool +ColorList::addColor(float red, float green, float blue) +{ + if (red < 0.0 || red > 1.0 || green < 0.0 || green > 1.0 || + blue < 0.0 || blue > 1.0) + return false; + + assert(_colors.size() > 0); + assert(_colors.last()->_scale == false); + _colors.last()->_list.append(new SbColor(red, green, blue)); + return true; +} + +bool +ColorList::addColor(float red, float green, float blue, double max) +{ + if (red < 0.0 || red > 1.0 || green < 0.0 || green > 1.0 || + blue < 0.0 || blue > 1.0) + return false; + + assert(_colors.size() > 0); + assert(_colors.last()->_scale == true); + _colors.last()->_list.append(new SbColor(red, green, blue)); + _colors.last()->_max.append(max); + return true; +} + +QTextStream& +operator<<(QTextStream& os, ColorList const& rhs) +{ + int i, j; + float r, g, b; + + for (i = 0; i < rhs.numLists(); i++) { + const ColorSpec &list = *(rhs._colors[i]); + os << '[' << i << "] = " << rhs._names[i] << ", scale = " + << (list._scale == true ? "true" : "false") << ": "; + if (list._list.size()) { + for (j = 0; j < list._list.size(); j++) { + list._list[j]->getValue(r, g, b); + os << r << ',' << g << ',' << b; + if (list._scale) + os << "<=" << list._max[j]; + os << ' '; + } + } + os << endl; + } + return os; +} + diff --git a/src/pmview/colorlist.h b/src/pmview/colorlist.h new file mode 100644 index 0000000..8f8a431 --- /dev/null +++ b/src/pmview/colorlist.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _COLORLIST_H_ +#define _COLORLIST_H_ + +#include "metriclist.h" +#include <QStringList> + +struct ColorSpec; + +typedef QList<ColorSpec *> ColorsList; + +class ColorList +{ +private: + + QStringList _names; + ColorsList _colors; + +public: + + virtual ~ColorList(); + + ColorList(); + + int numLists() const + { return _colors.size(); } + + const ColorSpec *list(const char *name); + + bool add(const char *name, const char *color = NULL); + bool add(const char *name, float red, float green, float blue); + + // Add colors + bool addColor(const char *color); + bool addColor(float red, float blue, float green); + + // Add scaled colors + bool addColor(const char *color, double max); + bool addColor(float red, float green, float blue, double max); + + static bool findColor(const char *color, float &r, float &g, float &b); + + friend QTextStream& operator<<(QTextStream& os, ColorList const& rhs); + +private: + bool findColor(const char *color); + + ColorList(ColorList const &); + ColorList const& operator=(ColorList const &); + // Never defined +}; + +struct ColorSpec +{ + bool _scale; + SbColorList _list; + QList<double> _max; + + ColorSpec(bool scale) : _scale(scale) {} +}; + +extern ColorList theColorLists; + +#endif /* _COLORLIST_H_ */ diff --git a/src/pmview/colormod.cpp b/src/pmview/colormod.cpp new file mode 100644 index 0000000..b2cefd9 --- /dev/null +++ b/src/pmview/colormod.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoSeparator.h> +#include "main.h" +#include "colormod.h" +#include "modlist.h" +#include "launch.h" + +#include <iostream> +using namespace std; + +ColorMod::~ColorMod() +{ +} + +ColorMod::ColorMod(const char *metric, double scale, + const ColorScale &colors, SoNode *obj) +: Modulate(metric, scale), + _state(Modulate::start), + _scale(colors), + _color(0) +{ + _root = new SoSeparator; + _color = new SoBaseColor; + + _color->rgb.setValue(_errorColor.getValue()); + _root->addChild(_color); + _root->addChild(obj); + + if (_metrics->numValues() == 1 && _scale.numSteps() && status() >= 0) { + add(); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorMod: Added " << metric << " (Id = " + << _root->getName().getString() << ")" << endl; +#endif + + } + else if (_metrics->numValues() > 1) { + warningMsg(_POS_, + "Color modulated metric (%s) has more than one value (%d)", + metric, _metrics->numValues()); + } + else if (_scale.numSteps() == 0) { + warningMsg(_POS_, + "No color steps for color modulated metric (%s)", + metric); + } +} + +void +ColorMod::refresh(bool fetchFlag) +{ + QmcMetric &metric = _metrics->metric(0); + + if (status() < 0) + return; + + if (fetchFlag) + metric.update(); + + if (metric.error(0) <= 0) { + if (_state != Modulate::error) { + _color->rgb.setValue(_errorColor.getValue()); + _state = Modulate::error; + } + } + else { + double value = metric.value(0) * theScale; + if (value > theNormError) { + if (_state != Modulate::saturated) { + _color->rgb.setValue(Modulate::_saturatedColor); + _state = Modulate::saturated; + } + } + else { + if (_state != Modulate::normal) + _state = Modulate::normal; + _color->rgb.setValue(_scale.step(value).color().getValue()); + } + } +} + +void +ColorMod::dump(QTextStream &os) const +{ + os << "ColorMod: "; + if (status() < 0) + os << "Invalid Metric: " << pmErrStr(status()) << endl; + else { + os << "state = "; + dumpState(os, _state); + os << ", scale = " << _scale << ": "; + _metrics->metric(0).dump(os, true); + } +} + +void +ColorMod::infoText(QString &str, bool) const +{ + const QmcMetric &metric = _metrics->metric(0); + str = metric.spec(true, true, 0); + str.append(QChar('\n')); + if (_state == Modulate::error) + str.append(theErrorText); + else if (_state == Modulate::start) + str.append(theStartText); + else { + QString value; + str.append(value.setNum(metric.realValue(0), 'g', 4)); + str.append(QChar(' ')); + if (metric.desc().units().length() > 0) + str.append(metric.desc().units()); + str.append(" ["); + str.append(value.setNum(metric.value(0) * 100.0 * theScale, 'g', 4)); + str.append("% of color scale]"); + } +} + +void +ColorMod::launch(Launch &launch, bool) const +{ + if (status() < 0) + return; + launch.startGroup("point"); + launch.addMetric(_metrics->metric(0), _scale, 0); + launch.endGroup(); +} + +int +ColorMod::select(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorMod::select: " << _metrics->metric(0) << endl; +#endif + return 1; +} + +int +ColorMod::remove(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorMod::remove: " << _metrics->metric(0) << endl; +#endif + return 0; +} diff --git a/src/pmview/colormod.h b/src/pmview/colormod.h new file mode 100644 index 0000000..c41f957 --- /dev/null +++ b/src/pmview/colormod.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _COLORMOD_H +#define _COLORMOD_H + +#include "colorscale.h" +#include "modulate.h" + +class SoBaseColor; +class SoNode; +class Launch; + +class ColorMod : public Modulate +{ +private: + + State _state; + ColorScale _scale; + SoBaseColor *_color; + +public: + + virtual ~ColorMod(); + + ColorMod(const char *metric, double scale, + const ColorScale &colors, SoNode *obj); + + virtual void refresh(bool fetchFlag); + + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void infoText(QString &str, bool) const; + + virtual void launch(Launch &launch, bool) const; + + virtual void dump(QTextStream &) const; + +private: + + ColorMod(); + ColorMod(const ColorMod &); + const ColorMod &operator=(const ColorMod &); + // Never defined +}; + +#endif /* _COLORMOD_H */ diff --git a/src/pmview/colorscale.cpp b/src/pmview/colorscale.cpp new file mode 100644 index 0000000..c90658d --- /dev/null +++ b/src/pmview/colorscale.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "main.h" +#include "colorscale.h" + +ColorStep::~ColorStep() +{ +} + +ColorStep::ColorStep(SbColor col, float val) +: _color(), + _min(val) +{ + _color.setValue(col.getValue()); +} + +ColorStep::ColorStep(float r, float g, float b, float val) +: _color(), + _min(val) +{ + SbColor tmp(r, g, b); + _color.setValue(tmp.getValue()); +} + +ColorStep::ColorStep(uint32_t col, float val) +: _color(), + _min(val) +{ + float dummy = 0.0; + _color.setPackedValue(col, dummy); +} + +ColorStep::ColorStep(const ColorStep &rhs) +: _color(), + _min(rhs._min) +{ + _color.setValue(rhs._color.getValue()); +} + +const ColorStep & +ColorStep::operator=(const ColorStep &rhs) +{ + if (this != &rhs) { + _color.setValue(rhs._color.getValue()); + _min = rhs._min; + } + return *this; +} + +ColorScale::~ColorScale() +{ + int i; + + for (i = 0; i < _colors.size(); i++) + delete _colors.takeAt(i); +} + +ColorScale::ColorScale(const SbColor &col) +: _colors() +{ + add(new ColorStep(col)); +} + +ColorScale::ColorScale(float r, float g, float b) +: _colors() +{ + add(new ColorStep(r, g, b)); +} + +ColorScale::ColorScale(uint32_t col) +: _colors() +{ + add(new ColorStep(col)); +} + +ColorScale::ColorScale(const ColorScale &rhs) +: _colors() +{ + int i; + + for (i = 0; i < rhs._colors.size(); i++) + add(new ColorStep(rhs[i])); +} + +const ColorScale & +ColorScale::operator=(const ColorScale &rhs) +{ + int i; + + if (this != &rhs) { + for (i = 0; i < _colors.size(); i++) + delete _colors.takeAt(i); + for (i = 0; i < rhs._colors.size(); i++) + add(new ColorStep(rhs[i])); + } + return *this; +} + +int +ColorScale::add(ColorStep *ptr) +{ + if (_colors.size()) { + float prev = _colors.last()->min(); + if (prev >= ptr->min()) { + warningMsg(_POS_, + "Color step (%f) was less than previous step (%f), skipping.", + ptr->min(), prev); + return -1; + } + } + _colors.append(ptr); + + return 0; +} + +const ColorStep & +ColorScale::step(float value) +{ + int i = _colors.size(); + + while (i > 0 && _colors[i-1]->min() > value) + i--; + + if (i == 0) + return *(_colors[0]); + return *(_colors[i-1]); +} + +QTextStream& +operator<<(QTextStream &os, const ColorScale &rhs) +{ + int i; + + if (rhs._colors.size() > 0) { + os << '[' << rhs[0].min(); + for (i = 1; i < rhs.numSteps(); i++) + os << ", " << rhs[i].min(); + os << ']'; + } + else { + os << "empty"; + } + + return os; +} + diff --git a/src/pmview/colorscale.h b/src/pmview/colorscale.h new file mode 100644 index 0000000..5578092 --- /dev/null +++ b/src/pmview/colorscale.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _COLORSCALE_H +#define _COLORSCALE_H + +#include <Inventor/SbColor.h> +#include <QtCore/QTextStream> +#include <QtCore/QList> + +class ColorStep +{ +private: + + SbColor _color; + float _min; + +public: + + ~ColorStep(); + + ColorStep(SbColor col, float val = 0.0); + ColorStep(float r, float g, float b, float val = 0.0); + ColorStep(uint32_t col, float val = 0.0); + ColorStep(const ColorStep &rhs); + + const ColorStep &operator=(const ColorStep &); + + const SbColor &color() const + { return _color; } + SbColor &color() + { return _color; } + + const float &min() const + { return _min; } + float &min() + { return _min; } +}; + +typedef QList<ColorStep *> ColorStepList; + +class ColorScale +{ +private: + + ColorStepList _colors; + +public: + + ~ColorScale(); + + ColorScale(const SbColor &col); + ColorScale(float r, float g, float b); + ColorScale(uint32_t col); + ColorScale(const ColorScale &); + const ColorScale &operator=(const ColorScale &); + + int numSteps() const + { return _colors.size(); } + + int add(ColorStep *ptr); + + const ColorStep &operator[](int i) const + { return *(_colors[i]); } + ColorStep &operator[](int i) + { return *(_colors[i]); } + + const ColorStep &step(float); + + friend QTextStream &operator<<(QTextStream &os, const ColorScale &rhs); + +private: + + ColorScale(); + // Not defined +}; + +#endif /* _COLORSCALE_H */ diff --git a/src/pmview/colorscalemod.cpp b/src/pmview/colorscalemod.cpp new file mode 100644 index 0000000..b162b49 --- /dev/null +++ b/src/pmview/colorscalemod.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoScale.h> +#include "main.h" +#include "colorscalemod.h" +#include "modlist.h" +#include "launch.h" + +#include <iostream> +using namespace std; + +ColorScaleMod::~ColorScaleMod() +{ +} + +ColorScaleMod::ColorScaleMod(const char *metric, double scale, + const ColorScale &colors, SoNode *obj, + float xScale, float yScale, float zScale) +: Modulate(metric, scale), + _state(Modulate::start), + _colScale(colors), + _scale(0), + _xScale(xScale), + _yScale(yScale), + _zScale(zScale), + _color(0) +{ + _root = new SoSeparator; + _color = new SoBaseColor; + + _color->rgb.setValue(_errorColor.getValue()); + _root->addChild(_color); + + if (_metrics->numValues() == 1 && _colScale.numSteps() && status() >= 0) { + + _scale = new SoScale; + _scale->scaleFactor.setValue(1.0, 1.0, 1.0); + _root->addChild(_scale); + _root->addChild(obj); + + add(); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorScaleMod: Added " << metric << " (Id = " + << _root->getName().getString() << ")" << endl; +#endif + + } + else if (_metrics->numValues() > 1) { + warningMsg(_POS_, + "Color modulated metric (%s) has more than one value (%d)", + metric, _metrics->numValues()); + _root->addChild(obj); + } + else if (status() < 0) + _root->addChild(obj); + else { + warningMsg(_POS_, + "No color steps for color modulated metric (%s)", + metric); + _root->addChild(obj); + } +} + +void +ColorScaleMod::refresh(bool fetchFlag) +{ + QmcMetric &metric = _metrics->metric(0); + + if (status() < 0) + return; + + if (fetchFlag) + metric.update(); + + if (metric.error(0) <= 0) { + if (_state != Modulate::error) { + _color->rgb.setValue(_errorColor.getValue()); + _scale->scaleFactor.setValue((_xScale==0.0f ? 1.0 : theMinScale), + (_yScale==0.0f ? 1.0 : theMinScale), + (_zScale==0.0f ? 1.0 : theMinScale)); + _state = Modulate::error; + } + } + else { + double value = metric.value(0) * theScale; + if (value > theNormError) { + if (_state != Modulate::saturated) { + _color->rgb.setValue(Modulate::_saturatedColor); + _scale->scaleFactor.setValue(1.0, 1.0, 1.0); + _state = Modulate::saturated; + } + } + else { + if (_state != Modulate::normal) + _state = Modulate::normal; + _color->rgb.setValue(_colScale.step(value).color().getValue()); + if (value < Modulate::theMinScale) + value = Modulate::theMinScale; + else if (value > 1.0) + value = 1.0; + _scale->scaleFactor.setValue((_xScale==0.0f ? 1.0 : _xScale*value), + (_yScale==0.0f ? 1.0 : _yScale*value), + (_zScale==0.0f ? 1.0 : _zScale*value)); + } + } +} + +void +ColorScaleMod::dump(QTextStream &os) const +{ + os << "ColorScaleMod: "; + if (status() < 0) + os << "Invalid Metric: " << pmErrStr(status()) << endl; + else { + os << "state = "; + dumpState(os, _state); + os << ", col scale = " << _colScale << ", y scale = " + << _scale->scaleFactor.getValue()[1] + << ": "; + _metrics->metric(0).dump(os, true); + } +} + +void +ColorScaleMod::infoText(QString &str, bool) const +{ + const QmcMetric &metric = _metrics->metric(0); + + str = metric.spec(true, true, 0); + str.append(QChar('\n')); + if (_state == Modulate::error) + str.append(theErrorText); + else if (_state == Modulate::start) + str.append(theStartText); + else { + QString value; + str.append(value.setNum(metric.realValue(0), 'g', 4)); + str.append(QChar(' ')); + if (metric.desc().units().length() > 0) + str.append(metric.desc().units()); + str.append(" ["); + str.append(value.setNum(metric.realValue(0) * 100.0 * theScale, 'g', 4)); + str.append("% of color scale]"); + } +} + +void +ColorScaleMod::launch(Launch &launch, bool) const +{ + if (status() < 0) + return; + launch.startGroup("point"); + launch.addMetric(_metrics->metric(0), _colScale, 0); + launch.endGroup(); +} + +int +ColorScaleMod::select(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorScaleMod::select: " << _metrics->metric(0) << endl; +#endif + return 1; +} + +int +ColorScaleMod::remove(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ColorScaleMod::remove: " << _metrics->metric(0) << endl; +#endif + return 0; +} diff --git a/src/pmview/colorscalemod.h b/src/pmview/colorscalemod.h new file mode 100644 index 0000000..66c670f --- /dev/null +++ b/src/pmview/colorscalemod.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _COLORSCALEMOD_H +#define _COLORSCALEMOD_H + +#include "colorscale.h" +#include "modulate.h" + +class SoBaseColor; +class SoScale; +class SoNode; +class Launch; + +class ColorScaleMod : public Modulate +{ +private: + + State _state; + ColorScale _colScale; + SoScale *_scale; + float _xScale; + float _yScale; + float _zScale; + SoBaseColor *_color; + +public: + + virtual ~ColorScaleMod(); + + ColorScaleMod(const char *metric, double scale, + const ColorScale &colors, SoNode *obj, + float xScale, float yScale, float zScale); + + virtual void refresh(bool fetchFlag); + + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void infoText(QString &str, bool) const; + + virtual void launch(Launch &launch, bool) const; + + virtual void dump(QTextStream &) const; + +private: + + ColorScaleMod(); + ColorScaleMod(const ColorScaleMod &); + const ColorScaleMod &operator=(const ColorScaleMod &); + // Never defined +}; + +#endif /* _COLORSCALEMOD_H */ diff --git a/src/pmview/defaultobj.cpp b/src/pmview/defaultobj.cpp new file mode 100644 index 0000000..977186e --- /dev/null +++ b/src/pmview/defaultobj.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "defaultobj.h" +#include "colorlist.h" +#include <QtCore/QSettings> + +#include <iostream> +using namespace std; + +DefaultObj *DefaultObj::theDefaultObj; + +DefaultObj::DefaultObj() +: _baseBorderX(8), + _baseBorderZ(8), + _baseHeight(6), + _barSpaceX(8), + _barSpaceZ(8), + _barSpaceLabel(6), + _barLength(28), + _barHeight(80), + _labelMargin(5), + _gridMinWidth(20), + _gridMinDepth(20) +{ + _baseColor[0] = _baseColor[1] = _baseColor[2] = 0.15; + _labelColor[0] = _labelColor[1] = _labelColor[2] = 1.0; + _pipeLength = _barHeight; +} + +const DefaultObj & +DefaultObj::defObj() +{ + if (!theDefaultObj) { + theDefaultObj = new DefaultObj; + theDefaultObj->getResources(); + } + return *theDefaultObj; +} + +DefaultObj & +DefaultObj::changeDefObj() +{ + if (!theDefaultObj) { + theDefaultObj = new DefaultObj; + theDefaultObj->getResources(); + } + return *theDefaultObj; +} + +DefaultObj::DefaultObj(const DefaultObj &rhs) +: _baseBorderX(rhs._baseBorderX), + _baseBorderZ(rhs._baseBorderZ), + _baseHeight(rhs._baseHeight), + _barSpaceX(rhs._barSpaceX), + _barSpaceZ(rhs._barSpaceZ), + _barSpaceLabel(rhs._barSpaceLabel), + _barLength(rhs._barLength), + _barHeight(rhs._barHeight), + _labelMargin(rhs._labelMargin), + _gridMinWidth(rhs._gridMinWidth), + _gridMinDepth(rhs._gridMinDepth), + _pipeLength(rhs._pipeLength) +{ + int i; + + for (i = 0; i < 3; i++) { + _baseColor[i] = rhs._baseColor[i]; + _labelColor[i] = rhs._labelColor[i]; + } +} + +const DefaultObj & +DefaultObj::operator=(const DefaultObj &rhs) +{ + int i; + + if (this != &rhs) { + _baseBorderX = rhs._baseBorderX; + _baseBorderZ = rhs._baseBorderZ; + _baseHeight = rhs._baseHeight; + _barSpaceX = rhs._barSpaceX; + _barSpaceZ = rhs._barSpaceZ; + _barSpaceLabel = rhs._barSpaceLabel; + _barLength = rhs._barLength; + _barHeight = rhs._barHeight; + _labelMargin = rhs._labelMargin; + _gridMinWidth = rhs._gridMinWidth; + _gridMinDepth = rhs._gridMinDepth; + + for (i = 0; i < 3; i++) { + _baseColor[i] = rhs._baseColor[i]; + _labelColor[i] = rhs._labelColor[i]; + } + } + return *this; +} + +QTextStream& +operator<<(QTextStream &os, const DefaultObj &rhs) +{ + os << "baseBorderX=" << rhs._baseBorderX; + os << ", baseBorderZ=" << rhs._baseBorderZ; + os << ", baseHeight=" << rhs._baseHeight; + os << ", baseColor=" << rhs._baseColor[0] << ',' << rhs._baseColor[1] + << ',' << rhs._baseColor[2] << endl; + os << ", barSpaceX=" << rhs._barSpaceX; + os << ", barSpaceZ=" << rhs._barSpaceZ; + os << ", barSpaceLabel=" << rhs._barSpaceLabel; + os << ", barLength=" << rhs._barLength; + os << ", barHeight=" << rhs._barHeight; + os << ", labelMargin=" << rhs._labelMargin; + os << ", labelColor=" << rhs._labelColor[0] << ',' << rhs._labelColor[1] + << ',' << rhs._labelColor[2] << endl; + os << ", gridMinWidth=" << rhs._gridMinWidth; + os << ", gridMinDepth=" << rhs._gridMinDepth; + return os; +} + +static void +getColorResource(const char *name, QString label, float &r, float &g, float &b) +{ + if (label != QString::null && label.compare("default") != 0) { + const char *str = (const char *)label.toAscii(); + if (ColorList::findColor(str, r, g, b) == false) { + pmprintf("%s: Unable to map color resource \"%s\" to \"%s\", " + "using default color rgbi:%f/%f/%f\n", + pmProgname, name, str, r, g, b); + } + } +} + +void +DefaultObj::getResources() +{ + QString color; + QSettings resources; + resources.beginGroup(pmProgname); + + _baseBorderX = resources.value("baseBorderWidth", 8).toInt(); + _baseBorderZ = resources.value("baseBorderDepth", 8).toInt(); + _baseHeight = resources.value("baseHeight", 2).toInt(); + color = resources.value("baseColor", QString("default")).toString(); + getColorResource("baseColor", color, + _baseColor[0], _baseColor[1], _baseColor[2]); + _barSpaceX = resources.value("barSpaceWidth", 8).toInt(); + _barSpaceZ = resources.value("barSpaceDepth", 8).toInt(); + _barSpaceLabel = resources.value("barSpaceLabel", 6).toInt(); + _barLength = resources.value("barLength", 28).toInt(); + _barHeight = resources.value("barHeight", 80).toInt(); + _labelMargin = resources.value("labelMargin", 5).toInt(); + color = resources.value("labelColor", QString("default")).toString(); + getColorResource("labelColor", color, + _labelColor[0], _labelColor[1], _labelColor[2]); + _gridMinWidth = resources.value("gridMinWidth", 20).toInt(); + _gridMinDepth = resources.value("gridMinDepth", 20).toInt(); + + resources.endGroup(); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "DefaultObj::getResources: " << *this << endl; +#endif +} diff --git a/src/pmview/defaultobj.h b/src/pmview/defaultobj.h new file mode 100644 index 0000000..f600790 --- /dev/null +++ b/src/pmview/defaultobj.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _DEFAULTOBJ_H_ +#define _DEFAULTOBJ_H_ + +#include "main.h" +#include <QtCore/QTextStream> + +class DefaultObj +{ +private: + + static DefaultObj *theDefaultObj; + + int _baseBorderX; + int _baseBorderZ; + int _baseHeight; + float _baseColor[3]; + + int _barSpaceX; + int _barSpaceZ; + int _barSpaceLabel; + int _barLength; + int _barHeight; + + int _labelMargin; + float _labelColor[3]; + + int _gridMinWidth; + int _gridMinDepth; + int _pipeLength; + +public: + + ~DefaultObj() + {} + + DefaultObj(); + DefaultObj(const DefaultObj &); + const DefaultObj &operator=(const DefaultObj &rhs); + + static const DefaultObj &defObj(); + + // Query + int baseBorderX() const + { return _baseBorderX; } + int baseBorderZ() const + { return _baseBorderZ; } + int baseHeight() const + { return _baseHeight; } + float baseColor(int i) const + { return _baseColor[i]; } + int barSpaceX() const + { return _barSpaceX; } + int barSpaceZ() const + { return _barSpaceZ; } + int barSpaceLabel() const + { return _barSpaceLabel; } + int barLength() const + { return _barLength; } + int barHeight() const + { return _barHeight; } + int labelMargin() const + { return _labelMargin; } + float labelColor(int i) const + { return _labelColor[i]; } + int gridMinWidth() const + { return _gridMinWidth; } + int gridMinDepth() const + { return _gridMinDepth; } + int pipeLength () const + { return _pipeLength; } + + // Local Changes + int &baseBorderX() + { return _baseBorderX; } + int & pipeLength () + { return _pipeLength; } + int &baseBorderZ() + { return _baseBorderZ; } + int &baseHeight() + { return _baseHeight; } + void baseColor(float r, float g, float b) + { _baseColor[0] = r; _baseColor[1] = g; _baseColor[2] = b; } + int &barSpaceX() + { return _barSpaceX; } + int &barSpaceZ() + { return _barSpaceZ; } + int &barSpaceLabel() + { return _barSpaceLabel; } + int &barLength() + { return _barLength; } + int &barHeight() + { return _barHeight; } + int &labelMargin() + { return _labelMargin; } + void labelColor(float r, float g, float b) + { _labelColor[0] = r; _labelColor[1] = g; _labelColor[2] = b; } + int &gridMinWidth() + { return _gridMinWidth; } + int &gridMinDepth() + { return _gridMinDepth; } + + // Global + static void baseBorderX(int val) + { changeDefObj()._baseBorderX = val; } + static void baseBorderZ(int val) + { changeDefObj()._baseBorderZ = val; } + static void baseHeight(int val) + { changeDefObj()._baseHeight = val; } + static void baseColors(float r, float g, float b) + { changeDefObj().baseColor(r, g, b); } + static void barSpaceX(int val) + { changeDefObj()._barSpaceX = val; } + static void barSpaceZ(int val) + { changeDefObj()._barSpaceZ = val; } + static void barSpaceLabel(int val) + { changeDefObj()._barSpaceLabel = val; } + static void barLength(int val) + { changeDefObj()._barLength = val; } + static void barHeight(int val) + { changeDefObj()._barHeight = val; } + static void labelMargin(int val) + { changeDefObj()._labelMargin = val; } + static void labelColors(float r, float g, float b) + { changeDefObj().labelColor(r, g, b); } + static void gridMinWidth(int val) + { changeDefObj()._gridMinWidth = val; } + static void gridMinDepth(int val) + { changeDefObj()._gridMinDepth = val; } + + friend QTextStream& operator<<(QTextStream &os, const DefaultObj &rhs); + +private: + + static DefaultObj &changeDefObj(); + void getResources(); +}; + +#endif /* _DEFAULTOBJ_H_ */ diff --git a/src/pmview/error.cpp b/src/pmview/error.cpp new file mode 100644 index 0000000..c5981d3 --- /dev/null +++ b/src/pmview/error.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "main.h" + +int errorCount; +int lineNum; + +void +yywarn(const char *s) +{ + const char * fmt = theConfigName.length() ? "%s: warning - %s(%d): %s\n": + "%s: warning - line %3$d: %4$s\n"; + const char * config = (const char *)theConfigName.toAscii(); + + pmprintf(fmt, pmProgname, config, lineNum+1, s); + pmflush(); +} + +void +yyerror(const char *s) +{ + const char * fmt = theConfigName.length() ? "%s: error - %s(%d): %s\n": + "%s: error - line %3$d: %4$s\n"; + const char * config = (const char *)theConfigName.toAscii(); + const char badeof[] = "unexpected end of file"; + + markpos(); + if (!locateError()) + s = (char *)badeof; + + pmprintf(fmt, pmProgname, config, lineNum+1, s); + pmflush(); + errorCount++; /* It's used in pmview.cpp to abort the execution */ +} + diff --git a/src/pmview/front-ends/GNUmakefile b/src/pmview/front-ends/GNUmakefile new file mode 100644 index 0000000..c67f02b --- /dev/null +++ b/src/pmview/front-ends/GNUmakefile @@ -0,0 +1,25 @@ +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +#VIEWDIR = $(PCP_VAR_DIR)/config/pmview +VIEWDIR = $(PCP_BIN_DIR) +LOGDIR = $(PCP_VAR_DIR)/config/pmlogger +ARGSDIR = $(PCP_SHARE_DIR)/lib + +VIEWS = clustervis dkvis mpvis nfsvis osvis weblogvis webpingvis webvis +LOGGERS = config.dkvis config.mpvis config.nfsvis config.osvis \ + config.weblogvis config.webpingvis config.webvis + +LSRCFILES = $(VIEWS) $(LOGGERS) pmview-args + +default build-me: + +include $(BUILDRULES) + +install: default + #$(INSTALL) -m 755 -d $(VIEWDIR) + $(INSTALL) -m 755 $(VIEWS) $(VIEWDIR) + $(INSTALL) -m 755 -d $(LOGDIR) + $(INSTALL) -m 444 $(LOGGERS) $(LOGDIR) + $(INSTALL) -m 755 -d $(ARGSDIR) + $(INSTALL) -m 755 pmview-args $(ARGSDIR)/pmview-args diff --git a/src/pmview/front-ends/clustervis b/src/pmview/front-ends/clustervis new file mode 100755 index 0000000..6cab7aa --- /dev/null +++ b/src/pmview/front-ends/clustervis @@ -0,0 +1,331 @@ +#!/bin/sh +# +# Copyright (c) 2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* +debug=false + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +# --- scaling parameters --- +# + +# maximum packets per second +max=750 + +# maximum req rate (default: 5% of packet rate) +maxreq=`expr $max / 20` + +# milliseconds per CPU +maxcpu=1000 + +# +# clustervis tolerates multiple hosts or archives +_pmview_multiple_sources_are_ok + +# --- define usage message --- +# +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] + +Default hosts are specified in /etc/nodes (or /etc/ace/nodes) +or specify the file specifying cluster hosts with the -H flag. +The -h flag may be used to specify multiple hosts or the -a flag +may be used to specify multiple archives. The -h, -a and -H flags +are all mutually exclusive. + +options: + -H nodes file specifying hosts in cluster + [default $PCP_CLUSTER_CONFIG or /etc/nodes or /etc/ace/nodes] + -m max maximum expected packets sent or received per sec [default 750] + -V verbose/diagnostic output + +pmview(1) options:' + + _pmview_usage >> $tmp.msg + echo >> $tmp.msg + echo >> $tmp.msg 'Default title is: Web Server Activity' + echo >> $tmp.msg ' + By default all network interfaces are shown, with a maximum packet rate + of '$max' packets per second. The maximum request rate is 5% (of the + maximum packet rate) and the maximum error rate is 20% of the maximum + request rate. + + If given, the [interface ...] regular expressions restrict + the network statistics displayed to matching network interface names only.' + + _pmview_info -f $tmp.msg +} + +# --- build WM_COMMAND X(1) property for restart after login/logout --- +# +echo -n "pmview Version 2.1 \"$prog\"" > $tmp.pmview +for arg +do + echo -n " \"$arg\"" >>$tmp.pmview +done +echo >> $tmp.pmview + +# --- parse command line arguments --- +# +verbose=false +argInterfaces="" +hosts="" +interfaces="" +numsource=1 +if [ -z "$PCP_CLUSTER_CONFIG" ] +then + nodesfile=/etc/nodes + [ ! -f "$nodesfile" ] && nodesfile=/etc/ace/nodes +else + nodesfile="$PCP_CLUSTER_CONFIG" + if [ ! -f "$nodesfile" ] + then + echo "Error: \"$nodesfile\" specified in \$PCP_CLUSTER_CONFIG: file not found" + _usage + exit 1 + fi +fi + +livemode=false +for f in $* +do + [ $f = "-h" ] && livemode=true +done + +_pmview_args "$@" +$debug && echo "DEBUG msource=\"$msource\"" + +if [ -n "$otherArgs" ] +then + while getopts "?H:m:v:V" c $otherArgs + do + case $c + in + H) + nodesfile=$OPTARG + if [ ! -f "$nodesfile" ] + then + _pmview_error "$prog: \"$nodesfile\" for -H file not found" + #NOTREACHED + fi + ;; + m) + max=$OPTARG + if [ "X-$max" != "X`expr 0 + -$max 2>/dev/null`" ] + then + _pmview_error "$prog: -m must have a positive integral argument" + #NOTREACHED + fi + maxreq=`expr $max / 20` + ;; + + V) + verbose=true + ;; + + '?') + _usage + exit 1 + ;; + esac + done +fi + +if [ $numsource -eq 0 ] +then + if [ -f $nodesfile ] + then + livemode=true + sourcelist=`sed -e '/^#/d' $nodesfile` + numsource=`echo $sourcelist | wc -w` + else + _pmview_error "$prog: -H, -a or -h must be given" + fi +fi + +$debug && echo DEBUG sourcelist=\"$sourcelist\" +hostcols=`echo "sqrt($numsource)" | bc` +$debug && echo hostcols=$hostcols + + +# --- set the window title --- +# +if [ -z "$titleArg" ] +then + titleArg="SGI PCP : Cluster Node Activity" +fi + + +# ---- the real config starts here --- +# pmview Version 2.1 +# +cat << end-of-file >> $tmp.pmview + +_stackLength 26 +_marginWidth 4 +_marginDepth 4 + +_colorList net_colors ( + rgbi:0.8/0.0/0.0 + rgbi:0.0/0.8/0.8 + rgbi:1.0/0.5/0.0 +) + +_colorList cpu_colors ( red2 blue2 blue2 ) + +# main grid +_grid _show ( + +end-of-file + +hostrow=0 +hostcol=0 +for source in $sourcelist +do + if $livemode + then + host=$source + msource="-h $source" + else + host=`pmdumplog -l $source | $PCP_AWK_PROG '/^Performance/ {print $NF}'` + msource="-a $source" + fi + + nice=`pmprobe $msource -i kernel.all.cpu.nice | $PCP_AWK_PROG '{print $2; exit}'` + if [ "$nice" = 1 ] + then + nicemetric=kernel.percpu.cpu.nice + else + nicemetric="" + fi + + _pmview_cache_fetch -v \ + kernel.percpu.cpu.user $nicemetric \ + network.interface.in.packets \ + network.interface.out.packets + + # --- how many CPUs on this system? --- + # + if _pmview_fetch -I kernel.percpu.cpu.user + then + ncpu=`wc -l <$tmp.pmview_result` + else + _pmview_fetch_fail "get the number of CPUs" + fi + + _pmview_cache_fetch -I network.interface.total.bytes + + # --- how many network interfaces on this system? --- + # + if _pmview_fetch -I network.interface.in.packets + then + nnet=`grep -v '^lo' $tmp.pmview_result | wc -l` + else + _pmview_fetch_fail "get the number of network interfaces" + fi + + rows=`echo "sqrt($ncpu + $ncpu + $nnet + $nnet)" | bc` + [ $rows -lt 4 ] && rows=4 + row=0 + col=0 + + echo "# grid for host $host" >>$tmp.pmview + echo "_baseHeight 8" >>$tmp.pmview + echo "_grid $hostcol $hostrow _hide (" >>$tmp.pmview + + echo " _baseColor rgbi:0.4/0.4/0.6" >>$tmp.pmview + echo " _label 0 0 _down _large \"$host\"" >>$tmp.pmview + echo " _grid 1 0 _show (" >>$tmp.pmview + + _pmview_fetch -I kernel.percpu.cpu.user + for cpu in `cat $tmp.pmview_result` + do + echo " _label $row $col _west \"$cpu\"" >>$tmp.pmview + echo " _stack `expr $row + 1` $col _hide (" >>$tmp.pmview + echo " _metrics (" >>$tmp.pmview + echo " $host:kernel.percpu.cpu.sys[$cpu] $maxcpu" >>$tmp.pmview + if [ ! -z "$nicemetric" ] + then + echo " $host:kernel.percpu.cpu.nice[$cpu] $maxcpu" >>$tmp.pmview + fi + echo " $host:kernel.percpu.cpu.user[$cpu] $maxcpu" >>$tmp.pmview + echo " )" >>$tmp.pmview + echo " _colorlist cpu_colors" >>$tmp.pmview + echo " )" >>$tmp.pmview + + $debug && echo stack at row=`expr $row + 1` col=$col \"$cpu\" + $debug && echo label at row=$row col=$col + + col=`expr $col + 1` + if [ $col -ge $rows ] + then + col=0 + row=`expr $row + 2` + fi + done + + _pmview_fetch -I network.interface.in.packets + grep -v '^lo' $tmp.pmview_result >$tmp.net + for net in `cat $tmp.net` + do + echo " _label $row $col _west \"$net\"" >>$tmp.pmview + echo " _stack `expr $row + 1` $col _hide (" >>$tmp.pmview + echo " _metrics (" >>$tmp.pmview + echo " $host:network.interface.total.errors[$net] $max" >>$tmp.pmview + echo " $host:network.interface.in.packets[$net] $max" >>$tmp.pmview + echo " $host:network.interface.out.packets[$net] $max" >>$tmp.pmview + echo " )" >>$tmp.pmview + echo " _colorlist net_colors" >>$tmp.pmview + echo " )" >>$tmp.pmview + + $debug && echo stack at row=`expr $row + 1` col=$col \"$net\" + $debug && echo label at row=$row col=$col + + col=`expr $col + 1` + if [ $col -ge $rows ] + then + col=0 + row=`expr $row + 2` + fi + + done + echo " )" >>$tmp.pmview + row=`expr $row + 1` + echo "# end of host grid" >>$tmp.pmview + echo ")" >>$tmp.pmview + + hostcol=`expr $hostcol + 1` + if [ $hostcol -ge $hostcols ] + then + hostcol=0 + hostrow=`expr $hostrow + 1` + fi + +done + +echo "" >>$tmp.pmview +echo "# end of main grid" >>$tmp.pmview +echo ")" >>$tmp.pmview + +$verbose && cat $tmp.pmview + +eval $PMVIEW <$tmp.pmview $args -title "'$titleArg'" -xrm "'*iconName: $prog'" + +exit + diff --git a/src/pmview/front-ends/config.clustervis b/src/pmview/front-ends/config.clustervis new file mode 100644 index 0000000..118cb34 --- /dev/null +++ b/src/pmview/front-ends/config.clustervis @@ -0,0 +1,13 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with clustervis(1) +# + +log advisory on default { + kernel.percpu.cpu.sys + kernel.percpu.cpu.user + network.interface.in.packets + network.interface.out.packets + network.interface.total.bytes + network.interface.total.errors +} diff --git a/src/pmview/front-ends/config.dkvis b/src/pmview/front-ends/config.dkvis new file mode 100755 index 0000000..03e0e31 --- /dev/null +++ b/src/pmview/front-ends/config.dkvis @@ -0,0 +1,14 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with dkvis(1) +# + +log advisory on default { + disk.dev.total + disk.dev.read + disk.dev.write + disk.dev.bytes + disk.dev.read_bytes + disk.dev.write_bytes + disk.dev.total_bytes +} diff --git a/src/pmview/front-ends/config.mpvis b/src/pmview/front-ends/config.mpvis new file mode 100755 index 0000000..f28ce84 --- /dev/null +++ b/src/pmview/front-ends/config.mpvis @@ -0,0 +1,13 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with mpvis(1) +# + +log advisory on default { + kernel.percpu.cpu.idle + kernel.percpu.cpu.intr + kernel.percpu.cpu.sys + kernel.percpu.cpu.sxbrk + kernel.percpu.cpu.user + kernel.percpu.cpu.wait.total +} diff --git a/src/pmview/front-ends/config.nfsvis b/src/pmview/front-ends/config.nfsvis new file mode 100755 index 0000000..f8ab0fb --- /dev/null +++ b/src/pmview/front-ends/config.nfsvis @@ -0,0 +1,12 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with nfsvis(1) +# + +log advisory on default { + nfs +} + +log advisory on default { + nfs3 +} diff --git a/src/pmview/front-ends/config.osvis b/src/pmview/front-ends/config.osvis new file mode 100755 index 0000000..629a3a4 --- /dev/null +++ b/src/pmview/front-ends/config.osvis @@ -0,0 +1,31 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with osvis(1) +# +log mandatory on once { + hinv.ncpu + hinv.ndisk + hinv.physmem + mem.physmem +} + +log advisory on default { + disk.all.avg_disk.active + disk.all.read + disk.all.write + disk.ctl.avg_disk.active + kernel.all.load + kernel.all.cpu.intr + kernel.all.cpu.sys + kernel.all.cpu.user + kernel.all.cpu.wait.total + mem.freemem + mem.util + network.interface.in.bytes + network.interface.in.packets + network.interface.in.errors + network.interface.out.bytes + network.interface.out.packets + network.interface.out.errors +} + diff --git a/src/pmview/front-ends/config.weblogvis b/src/pmview/front-ends/config.weblogvis new file mode 100755 index 0000000..1d3453f --- /dev/null +++ b/src/pmview/front-ends/config.weblogvis @@ -0,0 +1,20 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with weblogvis(1) +# + +log advisory on default { + web.perserver.logidletime + web.perserver.requests.total + web.perserver.bytes.total + web.perserver.requests.size.unknown + web.perserver.requests.size.gt3m + web.perserver.requests.size.le3m + web.perserver.requests.size.le1m + web.perserver.requests.size.le300k + web.perserver.requests.size.le100k + web.perserver.requests.size.le30k + web.perserver.requests.size.le10k + web.perserver.requests.size.le3k + web.perserver.requests.size.zero +} diff --git a/src/pmview/front-ends/config.webpingvis b/src/pmview/front-ends/config.webpingvis new file mode 100755 index 0000000..078ab3c --- /dev/null +++ b/src/pmview/front-ends/config.webpingvis @@ -0,0 +1,8 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with webpingvis(1) +# + +log advisory on default { + webpingvis +} diff --git a/src/pmview/front-ends/config.webvis b/src/pmview/front-ends/config.webvis new file mode 100644 index 0000000..08394fc --- /dev/null +++ b/src/pmview/front-ends/config.webvis @@ -0,0 +1,51 @@ +# +# pmlogger(1) configuration file suitable creating an archive to be +# used with webvis(1) +# + +# file system and hinv stats +# (log these once so they _definitely_ exist at start of archive) +log advisory on once { + hinv.ncpu + hinv.physmem + hinv.ndisk + mem.physmem + disk.dev.read +} + +log advisory on 60 seconds { + # Kernel, disk and swap and paging metrics + disk.all.read + disk.all.write + disk.all.avg_disk.active + kernel.all.cpu.idle + kernel.all.cpu.intr + kernel.all.cpu.sys + kernel.all.cpu.user + kernel.all.cpu.wait.total + mem.freemem + mem.util.kernel + mem.util.fs_ctl + mem.util.fs_dirty + mem.util.fs_clean + mem.util.user + swap.pagesout + network.interface.out.drops + network.interface.out.errors + network.interface.out.packets + network.interface.in.drops + network.interface.in.errors + network.interface.in.packets + network.interface.total.bytes + # Network data rates and error conditions + network.tcp.drops + network.tcp.conndrops + network.tcp.timeoutdrop + network.tcp.sndrexmitpack + network.tcp.rcvbadsum + network.tcp.rexmttimeo + network.mbuf.failed + network.mbuf.waited + # Web logs - both frequent and infrequent samples + web.allservers +} diff --git a/src/pmview/front-ends/dkvis b/src/pmview/front-ends/dkvis new file mode 100755 index 0000000..5cf7267 --- /dev/null +++ b/src/pmview/front-ends/dkvis @@ -0,0 +1,572 @@ +#! /bin/sh +# +# Copyright (c) 1997-2005 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] [diskid ...] + +options: + -b display data throughput (Kbytes/sec) rather than IOPs + -m maxrate maximum activity expected (integer) + [default 150 IOPs (without -b) or 2048 Kbytes/sec (with -b)] + -r display only the read activity + -V verbose/diagnostic output + -w display only the write activity + -X ndisk limit layout to at most this many disks per row, set + to 0 for no limit [default 12] + -Y nctl limit layout to at most this many controllers per column + before starting a new group of controllers, set to 0 + for no limit [default 16] + +pmview(1) options:' + + _pmview_usage >> $tmp.msg + echo >> $tmp.msg + echo 'Default title is: Total Disk Activity (IOPS) for Host' >> $tmp.msg + _pmview_info -f $tmp.msg +} + +verbose=false +type=total +Type=Total +Thru=IOPS +max='' +force=false +maxnctl=16 +maxndisk=12 +diskargs= +debug=false + +# build WM_COMMAND X(1) property for restart after login/logout +# +echo -n "pmview Version 2.1 \"dkvis\"" >$tmp.conf +for arg +do + echo -n " \"$arg\"" >>$tmp.conf +done + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?bdm:R:rv:VX:Y:w" c $otherArgs + do + case $c + in + b) + Thru="Kbytes/sec" + ;; + d) + debug=true + ;; + m) + _pmview_unsigned $c + max=$OPTARG + ;; + r) + if [ $type != total ] + then + _pmview_error "$prog: only one -r or -w option may be specified" + # NOTREACHED + fi + type=read + Type=Read + ;; + + v) + if [ $OPTARG = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + elif [ $OPTARG != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + + V) + verbose=true + ;; + w) + if [ $type != total ] + then + _pmview_error "$prog: only one -r or -w option may be specified" + # NOTREACHED + fi + type=write + Type=Write + ;; + X) + _pmview_unsigned $c + maxndisk=$OPTARG + ;; + [YR]) + # used to be -R, so support both for backwards compatibility + _pmview_unsigned $c + maxnctl=$OPTARG + force=true + ;; + ?) + _usage + exit 1 + ;; + + esac + done + set -- $otherArgs + shift `expr $OPTIND - 1` + + [ $# -gt 0 ] && diskargs=$@ +fi + +if [ "$Thru" != "IOPS" ] +then + case $type + in + read) + type=read_bytes + ;; + write) + type=write_bytes + ;; + *) + if pminfo $msource disk.dev.total_bytes >/dev/null 2>&1 + then + type=total_bytes + else + type=bytes + fi + ;; + esac +fi + +# default max depends on -b or not +# +if [ "$Thru" != "IOPS" ] +then + [ -z "$max" ] && max=2048 +else + [ -z "$max" ] && max=150 +fi + +pminfo -f $msource $namespace disk.dev.$type >$tmp.info 2>$tmp.err +if [ -s $tmp.err ] +then + cat $tmp.err | _pmview_fetch_mesg >> $tmp.msg + echo "$prog: Failed to get disk inventory from host \"$host\"" | fmt >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED +fi + +$PCP_AWK_PROG < $tmp.info -F'"' '/inst/ { print $2 }' > $tmp.disks + +if [ ! -s $tmp.disks ] +then + cat $tmp.info | _pmview_fetch_mesg >> $tmp.msg + echo "$prog: Failed to get disk inventory from host \"$host\"" | fmt >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED +fi + +if [ ! -z "$diskargs" ] +then + rm -f $tmp.tmp $tmp.msg + for dk in $diskargs + do + if echo "$dk" | grep '[.[^]' >/dev/null + then + # assume egrep(1) regular expression + # + if egrep "$dk" $tmp.disks >>$tmp.tmp + then + # found some matches + # + : + else + echo "$prog: pattern \"$dk\" does not match any disks ..." >$tmp.msg + fi + elif grep "^$dk\$" $tmp.disks >/dev/null + then + echo $dk >>$tmp.tmp + else + echo "$prog: disk \"$dk\" not in the disk inventory ..." >$tmp.msg + fi + done + if [ -s $tmp.msg ] + then + echo "Disks on host \"$host\" are:" >> $tmp.msg + fmt $tmp.disks | sed -e 's/^/ /' >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED + fi + mv $tmp.tmp $tmp.disks +fi + +if $debug +then + echo "Disks ..." + cat $tmp.disks +fi + +ndisk=`wc -l <$tmp.disks | sed -e 's/ //g'` + +if [ "$ndisk" -lt 1 ] +then + echo "$prog: Cannot get disk inventory for host \"$host\"\n" > $tmp.msg + cat $tmp.info >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED +fi + +cat << end-of-file >> $tmp.conf + +# +# dkvis +# +end-of-file + +if $verbose +then + echo "# Disk Inventory:" >> $tmp.conf + fmt <$tmp.disks | sed -e 's/^/# /' >> $tmp.conf +fi +cp $tmp.disks $tmp.in + +# get controller-based order +# +# the mapping is controlled by the here-is document below, where each +# (non-comment) line contains three fields separated by tabs: +# 1. pattern to match disk names (must match the start of a disk indom name) +# - don't bother escaping / (fixed up later) +# - <n> is expanded to \([0-9][0-9]*\) later +# - <h> is expanded to \([0-9a-f][0-9a-f]*\) later +# 2. controller-tag ... construction from literals and substrings (in sed +# syntax, so \1, \2, etc) from the pattern +# 3. sort field order ... constructed from the ordinal labels of the +# substrings in the pattern (comma separated) ... "n" is appended +# for numerical sorting +# +# so the control line +# dks<n>d<n>l*\([0-9]*\) dks\1 1n,2n,3n +# +# will first turn disk indom names like dks14d3, dks14d3l2, dks14d3l10, +# dks14d2, dks10d7, dks10d10 and dks10d1 into records like +# +# dks14 14 3 dks14d3 +# dks14 14 3 2 dks14d3l2 +# dks14 14 3 10 dks14d3l10 +# dks14 14 2 dks14d2 +# dks10 10 7 dks10d7 +# dks10 10 1 10 dks10d110 +# dks10 10 1 dks10d1 +# --+-- -+ -+ -+ ----+---- +# | | | | | +# | | | | +-- full disk name +# | | | +-- 3rd sort key (lun, may be missing) +# | | +-- 2nd sort key (target) +# | +-- 1st sort key (controller) +# +-- controller-tag for dkvis scene and used for grouping "related" +# disks +# +# and generate a sort(1) key of the form "+1n -2 +2n -3 +3n -4" +# +# after sorting this list becomes +# +# dks10 10 1 dks10d1 +# dks10 10 1 10 dks10d110 +# dks10 10 7 dks10d7 +# dks14 14 2 dks14d2 +# dks14 14 3 dks14d3 +# dks14 14 3 2 dks14d3l2 +# dks14 14 3 10 dks14d3l10 +# +# and after grouping based on the common controller-tag and stripping +# the sort fields this becomes +# +# dks10 dks10d1 dks10d110 dks10d7 +# dks14 dks14d2 dks14d3 dks14d3l2 dks14d3l10 +# +sed >$tmp.ctl <<'End-of-File' \ + -e '/^#/d' \ + -e 's/ */ /g' \ + -e 's/\//\\\//g' \ + -e 's/<n>/\\([0-9][0-9]*\\)/g' \ + -e 's/<h>/\\([0-9a-f][0-9a-f]*\\)/g' \ + -e 's/^/^/' +# Linux 2.2, no sard, IDE and SCSI +sd\([a-z]\)+hd\([abcd]\) sd+hd 1 +# Linux IDE, no devfs +hd\([abcd]\) hd 1 +# Linux IDE, with devfs +ide/host<n>/bus<n>/target<n>/lun<n> ide\1b\2 1n,2n,3n,4n +# Linux SCSI, no devfs +sd\([a-z]\) sd 1 +# Linux XSCSI, dual port HBA, with devfs +# e.g. xscsi/pci00.01.0-1/target2/lun0/disc (and similar for fabric device) +xscsi/pci<h>\.<h>\.<n>-<n>/target<n>/lun<n> xscsi\1.\2 1,2,3n,4n,5n,6n +xscsi/pci<h>\.<h>\.<n>-<n>/node<h>/lun<n> xscsi\1.\2 1,2,3n,4n,5,6n +# Linux XSCSI, single port HBA, with devfs +# e.g. xscsi/pci05.01.0/target2/lun0/disc (and similar for fabric device) +xscsi/pci<h>\.<h>\.<n>/target<n>/lun<n> xscsi\1.\2 1,2,3n,4n,5n +xscsi/pci<h>\.<h>\.<n>/node<h>/lun<n> xscsi\1.\2 1,2,3n,4,5n +# Linux SCSI, with devfs +scsi/host<n>/bus<n>/target<n>/lun<n> scsi\1b\2 1n,2n,3n,4n +# Linux Mylex RAID, no devfs +rd/c<n>d<n> dac 1n,2n +# Linux Mylex RAID, with devfs (old style) +dac960/c<n>d<n> dac 1n,2n +# Linux Mylex RAID, with devfs (new style) +dac960/host<n>/disc<n> dac 1n,2n +# IRIX Firewire +# two name formats: first is IRIX before 6.5.11, second is 6.5.11 or later +/hw/rdisk/1394/\([^/]*\)/lun<n>vol/c<n>p<n> 1394c\3 3n,2n,1,2n +1394/\([^/]*\)/lun<n>vol/c<n>p<n> 1394c\3 3n,2n,1,2n +# IRIX SCSI, note l<n> is missing for LUN 0, so pattern is a little different +# at the end +dks<n>d<n>l*\([0-9]*\) dks\1 1n,2n,3n +# IRIX FC +\(................\)/lun<n>/c<n>p<n> fc\3p\4 3n,4n,1,2n +# Linux direct attach xscsi +# e.g. xscsi/pci15.01.0-2/target4/lun0/disc +xscsi/pci<n>\.\(..\)\.\(.*\)/target<n>/lun<n> xscsi\1b\2 1n,2,3,4n,5n +# Linux fabric attach xscsi +# e.g. xscsi/pci04.01.0/node50060946700083e9/port1/lun0/disc +xscsi/pci<n>\.\(..\)\.\(.*\)/node\(................\)/port<n>/lun<n> xfc\1b\2 1n,2,3,4,5n,6n +# Linux IDA, with devfs +ida/disc<n> ida 1n +# Old style IRIX Jaguar drives +jag<n>d<n> jag\1 1n,2n +# Old style IRIX RAID drives +rad<n>d<n> rad\1 1n,2n +End-of-File + +if $debug +then + echo "Control lines ..." + cat $tmp.ctl +fi + +nctl=`wc -l <$tmp.ctl | sed -e 's/ //g'` +i=1 + +# loop once per contol line in $tmp.ctl ... +# - select matching disks +# - generate the sort keys with the disk names +# - sort and group by the controller-tag +# - remove the matching lists from consideration +# +while [ $i -le $nctl ] +do + $PCP_AWK_PROG -F' ' <$tmp.ctl ' +NR == '$i' { print "/" $1 "/p" >"'$tmp.sed-in'" + print "/" $1 "/!p" >"'$tmp.sed-out'" + nfld = split($3,fld,/,/) + maxfld = 1 + for (i = 1; i <= nfld; i++) { + if (fld[i] > maxfld) maxfld = fld[i] + printf "+%s -%d ",fld[i],fld[i]+1 >"'$tmp.sort-arg'" + } + print "" >"'$tmp.sort-arg'" + printf "%s","s/" $1 "/" $2 >"'$tmp.sed-key'" + for (i = 1; i <= maxfld; i++) { + printf " \\%d",i >"'$tmp.sed-key'" + } + print " &/" >"'$tmp.sed-key'" + exit + }' + + sed -n -f $tmp.sed-in <$tmp.in \ + | sed -f $tmp.sed-key >$tmp.key + sort `cat $tmp.sort-arg` $tmp.key \ + | $PCP_AWK_PROG >>$tmp.order ' +BEGIN { ctl = "" } +$1 != ctl { if (NR > 1) print "" + ctl = $1 + ndisk = 0 + printf "%s",ctl + } + { ndisk++ + if ('"$maxndisk"' > 0 && ndisk > '"$maxndisk"') { + print "" + printf "%s",ctl + ndisk = 1 + } + printf " %s",$NF + } +END { if (NR > 0) print "" }' + sed -n -f $tmp.sed-out <$tmp.in >$tmp.out + mv $tmp.out $tmp.in + + i=`expr $i + 1` +done + +# any leftovers do not match any control pattern +# +if [ -s $tmp.in ] +then + echo "$prog: The following disk names do not match a known controller pattern," >$tmp.warn + echo "and they will be ungrouped in the scene:" >>$tmp.warn + fmt <$tmp.in | sed -e 's/^/ /' >>$tmp.warn + _pmview_warning -f $tmp.warn + sed -e 's/.*/& &/' <$tmp.in >>$tmp.order +fi + +nctl=`wc -l <$tmp.order | sed -e 's/ //g'` + +# shape the base geometry for the scene ... there are groups, each with +# $maxnctl controller rows, and the groups are arranged in a grid that +# is $nrow groups deep and $ncol groups across +# +if [ $nctl -le $maxnctl ] +then + # less than $maxnctl controller rows, so just one group + # + ncol=1 + nrow=1 +else + # have some shaping to do ... assume each group is $maxnctl controller + # rows deep and as wide as the maximum number of disks per controller + # row, then square up the scene and maybe adjust $maxnctl + # + eval `$PCP_AWK_PROG <$tmp.order ' +START { maxdk = 0 } + { if (NF - 1 > maxdk) maxdk = NF - 1 } +END { + if ('"$nctl"' % "'$maxnctl'" == 0) + ngrp = '"$nctl"' / "'$maxnctl'" + else + ngrp = int('"$nctl"' / "'$maxnctl'") + 1 + # add 2 to maxdk as an estimate of the label length in units of + # displayed blocks, i.e. the label is about as long as the width + # of 2 blocks + ncol = int(0.5 + sqrt(ngrp)*'"$maxnctl"'/(maxdk + 2)) + if (ncol < 1) ncol = 1 + if (ngrp % ncol == 0) + nrow = ngrp / ncol + else + nrow = int(ngrp / ncol) + 1 + # use all of the space in the base layout + ngrp = nrow * ncol + # make all the groups have the same number of controller rows + if ('"$nctl"' % ngrp == 0) + maxnctl = '"$nctl"' / ngrp + else + maxnctl = int('"$nctl"' / ngrp) + 1 + print "ncol=" ncol " nrow=" nrow " maxnctl=" maxnctl + }'` +fi + +#DEBUG echo "nctl=$nctl maxnctl=$maxnctl ncol=$ncol nrow=$nrow" + +# heuristic hack for pmlaunch +# +if [ "$nctl" -gt 6 ] +then + group="_groupByInst" +else + group="_groupByMetric" +fi + +if [ -z "$titleArg" ] +then + titleArg="$Type Disk Activity ($Thru) for Host $host" +fi + +# +# pmview 2.0 +# + +echo ' +_grid (' >> $tmp.conf + +$PCP_AWK_PROG -v io=$Type -v max=$max -v group=$group -v thru="$Thru" <$tmp.order ' +BEGIN { row = 0; col = 0; cnt = 0; type = ""; ctl = "" + start_ctl = ""; label = ""; last_label = ""; last_ctl = "" + color[0] = "green" + color[1] = "blue" + color[2] = "red" + color[3] = "cyan" + color[4] = "violet" + color[5] = "yellow" + ncol = 5 + colorlist = "" + } + +function dumpLabel(start, last) +{ + printf(" _baseLabel \"%s Activity for Disks on ", io) + if (start != last) + printf("Controllers %s to %s", start, last) + else + printf("Controller %s", start) + printf("\\nNormalized to %s %s\"\n", max, thru) + printf(" _colorlist (%s )\n", colorlist ) + colorlist = "" +} + + { if (cnt % '"$maxnctl"' == 0) { + if (cnt > 0) { + print " )" + dumpLabel(start_ctl, last_ctl) + print " )" + } + printf("\n _bar %d %d north %s (\n", 2*col, 2*row, group) + print " _metrics (" + start_ctl = $1 + row++ + if (row >= '"$nrow"') { + col++ + row = 0 + } + } + printf(" disk.dev.'$type'[") + + for (d = 2; d <= NF; d++) { + if (d == 2) + printf("%s", $d) + else + printf(",%s", $d) + } + printf("] %s \"%s\"\n", max, $1) + if ($1 != last_ctl) { + # new controller, change colors + ncol++ + if (ncol > 5) + ncol = 0 + } + colorlist = colorlist " " color[ncol] + last_ctl = $1 + cnt++ + } +END { if (cnt > 0) { + print " )" + dumpLabel(start_ctl, last_ctl) + print " )" + } + print ")" + }' >> $tmp.conf + +$verbose && cat $tmp.conf + +eval $PMVIEW <$tmp.conf $args -title "'$titleArg'" # -xrm "'*iconName:dkvis'" + +exit diff --git a/src/pmview/front-ends/mpvis b/src/pmview/front-ends/mpvis new file mode 100755 index 0000000..dd7db78 --- /dev/null +++ b/src/pmview/front-ends/mpvis @@ -0,0 +1,452 @@ +#!/bin/sh +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +gridspace=120 +cpuargs= + +algorithm="k" +default_layout=true +force=false +maxrowlen=16 +verbose=false +showinst=false +version=2 + +_usage() +{ + echo >$tmp.msg 'Usage: mpvis [options] [cpuid...] + +options: + -b use row:column ratio of 1:8 (soft limit) + [default, if 64 or more CPUs] + -i show CPU numbers + -r rowlen maximum number of CPUs per row (soft limit) + [default 16, if less than 64 CPUs] + -R rowlen force this number of CPUs per row + -V verbose/diagnostic output + +pmview(1) options:' + + _pmview_usage >>$tmp.msg + echo >>$tmp.msg + echo 'Default title is: CPU Utilization for Host' >>$tmp.msg + _pmview_info -f $tmp.msg +} + +# generate one row of the scene +# +_do_row() +{ +if [ "$1" = "$2" ] +then + msg="$titleArg\n$1 only" +else + msg="$titleArg\n$1 to $2" +fi + +cat <<End-of-File >>$tmp.conf + _grid 0 $3 show ( + _baseLabel "$msg" + _bar $group ( + _metrics ( +End-of-File +$have_idle && echo " kernel.percpu.cpu.idle[$rowlist] $max_util \"idle\"" >>$tmp.conf +$have_wait && echo " kernel.percpu.cpu.wait.total[$rowlist] $max_util \"wait\"" >>$tmp.conf +$have_intr && echo " kernel.percpu.cpu.intr[$rowlist] $max_util \"intr\"" >>$tmp.conf +$have_nice && echo " kernel.percpu.cpu.nice[$rowlist] $max_util \"nice\"" >>$tmp.conf +$have_sys && echo " kernel.percpu.cpu.sys[$rowlist] $max_util \"sys\"" >>$tmp.conf +$have_user && echo " kernel.percpu.cpu.user[$rowlist] $max_util \"user\"" >>$tmp.conf +cat <<End-of-File >>$tmp.conf + ) +End-of-File +$showinst && echo " _instlabels away ( $labels )" >>$tmp.conf +cat <<End-of-File >>$tmp.conf + _colorlist cpu + _baseLabel "$msg" + ) + ) +End-of-File +} + +# build WM_COMMAND X(1) property for restart after login/logout +# +echo -n "pmview Version 2.1 \"mpvis\"" >$tmp.conf +for arg +do + echo -n " \"$arg\"" >>$tmp.conf +done + +_pmview_args "$@" + +if [ "X$otherArgs" != X ] +then + while getopts "bir:R:v:V?" c $otherArgs + do + case $c + in + b) + algorithm="b" + default_layout=false + ;; + i) + showinst=true + gridspace=60 + ;; + r) + _pmview_unsigned $c + maxrowlen=$OPTARG + default_layout=false + ;; + R) + _pmview_unsigned $c + maxrowlen=$OPTARG + default_layout=false + force=true + ;; + + v) + version=$OPTARG + if [ $version = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + version=2 + elif [ $version != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + + V) + verbose=true + ;; + ?) + _usage + exit 1 + ;; + esac + done + set -- $otherArgs + shift `expr $OPTIND - 1` + + [ $# -gt 0 ] && cpuargs=$@ +fi + +[ -z "$titleArg" ] && titleArg="SGI PCP : CPU Utilization for Host $host" + +_pmview_cache_fetch -I kernel.percpu.cpu.user +_pmview_cache_fetch -v kernel.percpu.cpu.idle \ + kernel.percpu.cpu.wait.total \ + kernel.percpu.cpu.intr \ + kernel.percpu.cpu.nice \ + kernel.percpu.cpu.sys \ + kernel.percpu.cpu.user + + +# Check that we can get the metrics +# +if _pmview_fetch_indom kernel.percpu.cpu.user +then + if [ ! -s "$tmp.pmview_result" -o "$number" -lt 1 ] + then + _pmview_fetch_fail "get CPU inventory" + fi +else + _pmview_fetch_fail "get CPU inventory" + # NOTREACHED +fi + +if [ ! -z "$cpuargs" ] +then + # restrict based on command line args + # + rm -f $tmp.tmp $tmp.msg + ncpu=0 + for cpu in $cpuargs + do + if echo "$cpu" | grep '[.[^]' >/dev/null + then + # assume egrep(1) regular expression + # + if egrep "$cpu" $tmp.pmview_result >>$tmp.tmp + then + # found some matches + # + : + else + echo "$prog: pattern \"$cpu\" does not match any CPUs ..." >$tmp.msg + fi + elif grep "^$cpu\$" $tmp.pmview_result >/dev/null + then + echo $cpu >>$tmp.tmp + else + echo "$prog: CPU \"$cpu\" not in the CPU inventory" >$tmp.msg + fi + done + if [ -s $tmp.msg ] + then + echo "CPUs on host \"$host\" are:" >> $tmp.msg + tr < $tmp.pmview_result '\012' ' ' | fmt | sed -e "s/^/ /" >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED + fi + sort $tmp.tmp | uniq > $tmp.pmview_result + ncpu=`wc -l $tmp.pmview_result | $PCP_AWK_PROG '{print $1}'` +else + ncpu=$number +fi + +if [ "$ncpu" -ge 64 -a "$default_layout" = true ] +then + # >= 64p, no -b, -r or -R options ... make -b the default + # + algorithm="b" +fi + +# sort list +# +if grep cpu: $tmp.pmview_result >/dev/null +then + # Origin series name style + sed -e 's/:/./' < $tmp.pmview_result \ + | sort -t. +1n -2 +2n -3 +3 -4 \ + | sed -e 's/\./:/' \ + > $tmp.cpulist +else + # CPU names for older systems + sed -e 's/cpu/cpu./' < $tmp.pmview_result \ + | sort -t. +1n -2 \ + | sed -e 's/\.//' \ + > $tmp.cpulist +fi + +scale='' +have_idle=false +if _pmview_fetch_values kernel.percpu.cpu.idle +then + have_idle=true + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.idle| sed -n '/Semantics:/s/.*Units: //p'` +fi + +have_wait=false +if _pmview_fetch_values kernel.percpu.cpu.wait.total +then + have_wait=true + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.wait.total | sed -n '/Semantics:/s/.*Units: //p'` +fi + +have_intr=false +if _pmview_fetch_values kernel.percpu.cpu.intr +then +# linux 2.6 has wait and intr, but 2.4 does not + have_intr=`$PCP_AWK_PROG -v found=false ' + $1 > 0 { found="true" } + END { print found }' $tmp.pmview_result` + have_wait=$have_intr + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.intr | sed -n '/Semantics:/s/.*Units: //p'` +fi + +have_nice=false +if _pmview_fetch_values kernel.percpu.cpu.nice +then + have_nice=true + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.nice | sed -n '/Semantics:/s/.*Units: //p'` +fi + +have_sys=false +if _pmview_fetch_values kernel.percpu.cpu.sys +then + have_sys=true + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.sys | sed -n '/Semantics:/s/.*Units: //p'` +fi + +have_user=false +if _pmview_fetch_values kernel.percpu.cpu.user +then + have_user=true + [ -z "$scale" ] && scale=`pminfo $namespace $msource -d kernel.percpu.cpu.user | sed -n '/Semantics:/s/.*Units: //p'` +fi + +case $scale +in + microsec) + max_util=1000000 + ;; + millisec) + max_util=1000 + ;; + *) + _pmview_warning "$prog: cannot determine CPU time units, assuming milliseconds" + max_util=1000 + ;; +esac + +# shape the base geometry for the scene +# +if [ $ncpu -le "$maxrowlen" ] +then + nrows=1 + ncols=$ncpu +elif $force +then + nrows=`echo $ncpu $maxrowlen | $PCP_AWK_PROG ' \ + { x = $1 / $2; y = $1 % $2; if (y > 0) ++x; printf "%d\n", x; }'` + ncols=$maxrowlen +else + case $algorithm in + a) # this algorithm doesn't work at the moment + # (the exit condition is not robust enough) + nrows=1 + ncols=1 + bound=1 + num=0 + while [ $num -gt $ncols -o $num -lt $bound ] + do + nrows=`expr $nrows \* 2` + ncols=`expr $ncpu / $nrows` + bound=`expr $ncols / 2` + num=`expr $nrows \* 4` + done + ncols=`echo $ncpu $nrows | $PCP_AWK_PROG ' { x = $1 / $2; y = $1 % $2; \ + if (y > 0) ++x; printf "%d\n", x }'` + ;; + b) + # use a ratio for rows:columns of 1:8 + # + nrows=`echo $ncpu | $PCP_AWK_PROG ' { x = sqrt ($1 / 8.0); + y = int (x); if (y < x) ++y; print y }'` + ncols=`expr $ncpu + $nrows - 1` + ncols=`expr $ncols / $nrows` + ;; + k) + nrows=`expr $ncpu + $maxrowlen - 1` + nrows=`expr $nrows / $maxrowlen` + ncols=`expr $ncpu + $nrows - 1` + ncols=`expr $ncols / $nrows` + esac +fi + +if [ "$ncols" -gt 6 ] +then + group="_groupByMetric" +else + group="_groupByInst" +fi + +cat <<End-of-File >>$tmp.conf + +# +# mpvis +# +# ncpus = $ncpu +# nrows = $nrows +# ncols = $ncols +# +# List: +End-of-File + +col=0 +rowlist="" +cat $tmp.cpulist | while read cpu +do + if [ $col -eq 0 ] + then + echo -n "$cpu " > $tmp.rowlist + else + echo -n "$cpu " >> $tmp.rowlist + fi + col=`expr $col + 1` + if [ "$col" -eq $ncols ] + then + echo "# "`cat $tmp.rowlist` >>$tmp.conf + col=0 + rm -f $tmp.rowlist + fi +done + +[ -s $tmp.rowlist ] && echo "# "`cat $tmp.rowlist` >>$tmp.conf + +echo "_gridSpace $gridspace" >>$tmp.conf +echo >>$tmp.conf + +colorlist="_colorlist cpu (" +$have_idle && colorlist="$colorlist green2" +$have_wait && colorlist="$colorlist cyan2" +$have_intr && colorlist="$colorlist yellow2" +$have_nice && colorlist="$colorlist rgbi:0.6/1.0/0.7" +$have_sys && colorlist="$colorlist red2" +$have_user && colorlist="$colorlist blue2" +colorlist="$colorlist )" + +echo "$colorlist" >>$tmp.conf +echo "_grid 0 0 _hide ( # outer grid" >>$tmp.conf + +# build rows from front-to-back of scene +# fill rows with CPUs from left-to-right +# +y=`expr $nrows \* 2 - 2` +col=0 +rowlist="" +labels="" +cat $tmp.cpulist | while read cpu +do + if [ $col -eq 0 ] + then + rowlist=$cpu + start=$cpu + else + rowlist="$rowlist,$cpu" + fi + $showinst && labels="$labels \"`echo $cpu | sed -e 's/cpu:*//'`\"" + col=`expr $col + 1` + echo "$start $cpu $y $rowlist $labels" > $tmp.rowlist + if [ $col -eq $ncols ] + then + _do_row $start $cpu $y + col=0 + echo -n "" > $tmp.rowlist + labels="" + y=`expr $y - 2` + fi +done + +if [ -s $tmp.rowlist ] +then + read start cpu y rowlist labels < $tmp.rowlist + # cat $tmp.rowlist + _do_row $start $cpu $y +fi + +echo ")" >>$tmp.conf + +if [ $nrows -eq 1 ] +then + # remove unnecessary _grid for a single row + # + sed -e '/^ _grid/d' -e '/^ )/d' <$tmp.conf >$tmp.tmp + mv $tmp.tmp $tmp.conf +fi + +$verbose && cat $tmp.conf + +eval $PMVIEW <$tmp.conf $args -title "'$titleArg'" -xrm "'*iconName:mpvis'" + +exit diff --git a/src/pmview/front-ends/nfsvis b/src/pmview/front-ends/nfsvis new file mode 100755 index 0000000..26b87ae --- /dev/null +++ b/src/pmview/front-ends/nfsvis @@ -0,0 +1,244 @@ +#! /bin/sh +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] + +options: + -c version Monitor NFS version (integer) client metrics [default 2] + -m maxrate maximum request rate expected (integer) [default 120] + -s version Monitor NFS version (integer) server metrics [default 2] + -V verbose/diagnostic output + +pmview(1) options:' + + _pmview_usage >>$tmp.msg + echo >>$tmp.msg + echo 'Default title is: NFS Version 2 Request Traffic for host' >>$tmp.msg + _pmview_info -f $tmp.msg +} + +max=120 +client=2 +server=2 +type=total +Type=Total +verbose=false +version=2 + +# build WM_COMMAND X(1) property for restart after login/logout +# +echo -n "pmview Version 2.1 \"nfsvis\"" >$tmp.conf +for arg +do + echo -n " \"$arg\"" >>$tmp.conf +done + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?c:m:s:v:V" c $otherArgs + do + case $c + in + c) + client=$OPTARG + if [ "$client" != 2 -a "$client" != 3 ] + then + _pmview_error "$prog: only NFS 2 and NFS 3 client metrics supported" + # NOTREACHED + fi + ;; + m) + _pmview_unsigned $c + max=$OPTARG + ;; + s) + server=$OPTARG + if [ "$server" != 2 -a "$server" != 3 ] + then + _pmview_error "$prog: only NFS 2 and NFS 3 server metrics supported" + # NOTREACHED + fi + ;; + + v) + version=$OPTARG + if [ $version = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + version=2 + elif [ $version != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + + V) + verbose=true + ;; + ?) + _usage + exit 1 + ;; + esac + done + set - $otherArgs + shift `expr $OPTIND - 1` + if [ $# -gt 0 ] + then + _usage + exit 1 + fi +fi + +if [ "$client" = "2" ] +then + # NFS V2 client stats + # + if _pmview_fetch_indom nfs.client.reqs + then + : + else + _pmview_fetch_fail "get NFS Version 2 client metrics" + # NOTREACHED + fi +else + # NFS V3 client stats + # + if _pmview_fetch_indom nfs3.client.reqs + then + : + else + _pmview_fetch_fail "get NFS Version 3 client metrics" + # NOTREACHED + fi +fi + +# handle fsstat alias statfs +# +c_statfs=statfs +grep fsstat $tmp.pmview_result >/dev/null && c_statfs=fsstat + +if [ "$server" = "2" ] +then + # NFS V2 server stats + # + if _pmview_fetch_indom nfs.server.reqs + then + : + else + _pmview_fetch_fail "get NFS Version 2 server metrics" + # NOTREACHED + fi +else + # NFS V3 server stats + # + if _pmview_fetch_indom nfs3.server.reqs + then + : + else + _pmview_fetch_fail "get NFS Verion 3 server metrics" + # NOTREACHED + fi +fi + +# handle fsstat alias statfs +# +s_statfs=statfs +grep fsstat $tmp.pmview_result >/dev/null && s_statfs=fsstat + +if [ -z "$titleArg" ] +then + titleArg="SGI PCP : NFS Client V$client & Server V$server Request Traffic for host $host" +fi + + +cat << End-of-File >> $tmp.conf + +# +# nfsvis +# +_colorlist colors (red1 green1 blue1) +_grid hide ( + _label 2 0 _down _large "Client" + _bar 0 0 _east _groupByMetric ( + _metrics ( +End-of-File + +if [ "$client" = "2" ] +then + cat << End-of-File >> $tmp.conf + nfs.client.reqs[create,remove,rename,link,symlink,mkdir,rmdir] $max "dir" + nfs.client.reqs[getattr,setattr,lookup,readdir,$c_statfs,root] $max "attr" + nfs.client.reqs[readlink,read,write,wrcache] $max "data" + ) + _baseLabel "Requests by NFS2 Client\nNormalized to $max requests / second" +End-of-File +else + cat << End-of-File >> $tmp.conf + nfs3.client.reqs[create,remove,rename,link,symlink,mkdir,rmdir,mknod] $max "dir" + nfs3.client.reqs[getattr,setattr,lookup,readdir,$c_statfs,access,readdir+,fsinfo,pathconf] $max "attr" + nfs3.client.reqs[readlink,read,write,commit] $max "data" + ) + _baseLabel "Requests by NFS3 Client\nNormalized to $max requests / second" +End-of-File +fi +cat << End-of-File >> $tmp.conf + _colorlist colors + ) + _label 2 2 _down _large "Server" + _bar 0 2 _east ( + _metrics ( +End-of-File +if [ "$server" = "2" ] +then + cat << End-of-File >> $tmp.conf + nfs.server.reqs[create,remove,rename,link,symlink,mkdir,rmdir] $max "dir" + nfs.server.reqs[getattr,setattr,lookup,readdir,$s_statfs,root] $max "attr" + nfs.server.reqs[readlink,read,write,wrcache] $max "data" + ) + _baseLabel "Requests to NFS2 Server\nNormalized to $max requests / second" +End-of-File +else + cat << End-of-File >> $tmp.conf + nfs3.server.reqs[create,remove,rename,link,symlink,mkdir,rmdir,mknod] $max "dir" + nfs3.server.reqs[getattr,setattr,lookup,readdir,$s_statfs,access,readdir+,fsinfo,pathconf] $max "attr" + nfs3.server.reqs[readlink,read,write,commit] $max "data" + ) + _baseLabel "Requests to NFS3 Server\nNormalized to $max requests / second" +End-of-File +fi + +cat << End-of-File >> $tmp.conf + _colorlist colors + ) +) +End-of-File + +$verbose && cat $tmp.conf + +eval $PMVIEW <$tmp.conf $args -title "'$titleArg'" -xrm "'*iconName:nfsvis'" + +exit diff --git a/src/pmview/front-ends/osvis b/src/pmview/front-ends/osvis new file mode 100644 index 0000000..a704446 --- /dev/null +++ b/src/pmview/front-ends/osvis @@ -0,0 +1,651 @@ +#! /bin/sh +# Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +# +# scaling parameters +# + +# maximum network packets per second +maxpack=750 + +# maximum error packets per second +maxerr=`expr $maxpack / 100` + +# maximum network bytes per second +maxbyte=65536 + +# maximum disk io rate (I/O operations per second) +maxio=100 + +# milliseconds per CPU +maxcpu=1000 + +# maximum disk activity +maxdisk=30 + +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] [interface [interface ...]] + +options: + -b bytes maximum expected network throughput [default 65536 bytes] + -d activity maximum expected disk activity as a percentage [default 30] + -i ops maximum expected I/O operations per seconds [default 100] + -m packets maximum expected packets sent or received per sec [default 750] + -V verbose/diagnostic output + +pmview(1) options:' + _pmview_usage >>$tmp.msg + echo >> $tmp.msg + echo 'Default title is: High-Level Activity for Host' >> $tmp.msg + + echo >>$tmp.msg ' + By default all network interfaces are shown, with a maximum packet rate + of '$maxpack' packets per second and error rate of '$maxerr' packets per second. + + If given, the [interface [interface ...]] regular expressions restrict + the network statistics displayed to matching network interface names only.' + + _pmview_info -f $tmp.msg +} + +argInterfaces="" +verbose=false +interfaces="" + +# build WM_COMMAND X(1) property for restart after login/logout +# +echo -n "pmview Version 2.1 \"osvis\"" >$tmp.conf +for arg +do + echo -n " \"$arg\"" >>$tmp.conf +done + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?b:d:i:m:r:v:V" c $otherArgs + do + case $c + in + + b) + _pmview_unsigned $c + maxbyte=$OPTARG + ;; + + d) + _pmview_unsigned $c + maxdisk=$OPTARG + ;; + + i) + _pmview_unsigned $c + maxio=$OPTARG + ;; + + m) + _pmview_unsigned $c + maxpack=$OPTARG + ;; + + V) + verbose=true + ;; + + '?') + _usage + exit 1 + ;; + esac + done + + set -- $otherArgs + shift `expr $OPTIND - 1` + if [ $# -gt 0 ] + then + argInterfaces="$*" + fi +fi + +cat << end-of-file >> $tmp.conf + +# +# osvis +# +end-of-file + +# maximum req error rate (default: 1% of packet rate) +maxerr=`expr $maxpack / 100` +[ "$maxerr" -eq 0 ] && maxerr=1 + +_pmview_cache_fetch -v hinv.ncpu \ + hinv.ndisk \ + disk.all.avg_disk.active \ + mem.util \ + hinv.physmem \ + mem.physmem \ + kernel.all.cpu.wait.total \ + kernel.all.cpu.intr \ + kernel.all.cpu.nice \ + kernel.all.cpu.sys \ + kernel.all.cpu.user + +_pmview_cache_fetch -I network.interface.in.bytes \ + disk.ctl.avg_disk.active + +if _pmview_fetch_values hinv.ncpu +then + ncpu=`cat $tmp.pmview_result` + maxcpu=`expr $maxcpu \* $ncpu` + maxload=`expr $ncpu \* 2` +else + _pmview_fetch_fail "get the number of CPUs" +fi + +if [ $ncpu -eq 1 ] +then + cpudesc="1 CPU" +else + cpudesc="$ncpu CPUs" +fi + +$verbose && echo "# $cpudesc detected" >> $tmp.conf + +if _pmview_fetch_indom network.interface.in.bytes +then + ninterfaces=$number +else + _pmview_fetch_warn "get the number of network interfaces" + ninterfaces=0 +fi + +[ $ninterfaces -gt 0 ] && sed < $tmp.pmview_result -e 's/lo[0-9]*//g' -e 's/sl[0-9]*//g' -e 's/ppp[0-9]*//g' > $tmp.list + +[ ! -s $tmp.list ] && ninterfaces=0 + +if $verbose +then + echo "# Available Network Interfaces: "`cat $tmp.list | tr '\012' ' '` >> $tmp.conf +fi + +if [ $ninterfaces -gt 0 ] +then + if [ -z "$argInterfaces" ] + then + cp $tmp.list $tmp.chosen + else + touch $tmp.chosen + for i in $argInterfaces + do + egrep $i $tmp.list >> $tmp.chosen + done + fi + + interfaces_sp=`cat $tmp.chosen | sort | uniq` + interfaces=`echo $interfaces_sp | sed -e 's/ /,/g'` + ninterfaces=`echo $interfaces | wc -w` + + if $verbose + then + echo "# Network interfaces Matching \"$argInterfaces\": $interfaces" >> $tmp.conf + fi + + if [ $ninterfaces -eq 0 ] + then + echo "$prog: no matching network interfaces for \"$argInterfaces\"" > $tmp.msg + echo "Available network interfaces on host \"$host\" are: " >> $tmp.msg + tr < $tmp.list '\012' ' ' | fmt | sed -e "s/^/ /" >> $tmp.msg + _pmview_error -f $tmp.msg + #NOTREACHED + fi +fi + +if _pmview_fetch_values hinv.ndisk +then + ndisk=`cat $tmp.pmview_result` + maxdiskscale=`expr $maxdisk \* 10` +else + ndisk=0 + maxdiskscale=0 +fi + +if [ $ndisk -eq 1 ] +then + diskdesc="1 Disk" +else + diskdesc="$ndisk Disks" +fi + +$verbose && echo "# $diskdesc detected" >> $tmp.conf + +if [ "$ndisk" -gt 0 ] +then + + if _pmview_fetch_values disk.all.avg_disk.active + then + allAvg=true + else + allAvg=false + fi + + if _pmview_fetch_indom disk.ctl.avg_disk.active + then + nctrl=$number + cp $tmp.pmview_result $tmp.ctrls + else + nctrl=0 + fi + + if [ $nctrl -eq 1 ] + then + ctrldesc="1 Disk Controller" + else + ctrldesc="$nctrl Disk Controllers" + fi + + if [ "$nctrl" -gt 0 ] + then + + collen=`expr $ninterfaces + 3` + collensq=`expr $collen \* $collen` + + if [ $nctrl -le $collen ] + then + ctrlcols=$nctrl + elif [ $nctrl -le $collensq ] + then + ctrlcols=$collen + else + ctrlcols=`echo $nctrl \ + | $PCP_AWK_PROG '{x = sqrt($1); y = int(x); if (y < x) y++; print y }'` + fi + fi + + if $verbose + then + echo "# $ctrldesc detected: "`tr < $tmp.ctrls ' ' '\012'` >> $tmp.conf + echo "#" >> $tmp.conf + fi +fi + +linuxutilmem=false +utilmem=false +if _pmview_fetch_values mem.util.used +then + linuxutilmem=true + if $verbose + then + echo "# Linux memory utilization metrics supported" >> $tmp.conf + echo "#" >> $tmp.conf + fi +else + linuxutilmem=false + if _pmview_fetch_values mem.util + then + utilmem=true + if $verbose + then + echo "# Memory utilization metrics supported" >> $tmp.conf + echo "#" >> $tmp.conf + fi + else + utilmem=false + if $verbose + then + _pmview_warning "$prog: Memory utilization metrics not supported, showing free memory only" + echo "# Memory utilization metrics not supported, showing free memory only" >> $tmp.conf + echo "#" >> $tmp.conf + fi + fi +fi + +# Use mem.physmem if available, otherwise hinv.physmem will do. +if _pmview_fetch_values mem.physmem +then + realmem=`cat $tmp.pmview_result` +elif _pmview_fetch_values hinv.physmem +then + realmem=`cat $tmp.pmview_result` + realmem=`expr $realmem \* 1024` +else + realmem=0 + + if $utilmem + then + if $verbose + then + echo "# Unable to determine total real memory" >> $tmp.conf + echo "# Showing memory utilisation with free memory" >> $tmp.conf + fi + else + _pmview_warning "Unable to determine size of real memory on host \"$host\"" + fi +fi + +have_wait=false +if _pmview_fetch_values kernel.all.cpu.wait.total +then + have_wait=true +fi + +have_intr=false +if _pmview_fetch_values kernel.all.cpu.intr +then + have_intr=true +fi + +have_nice=false +if _pmview_fetch_values kernel.all.cpu.nice +then + have_nice=true +# linux hack !! +# these metrics are not actually supported in linux + have_intr=false + have_wait=false +fi + +have_sys=false +if _pmview_fetch_values kernel.all.cpu.sys +then + have_sys=true +fi + +have_user=false +if _pmview_fetch_values kernel.all.cpu.user +then + have_user=true +fi + +cpucolors="(" +$have_user && cpucolors="$cpucolors blue2" +$have_sys && cpucolors="$cpucolors red2" +$have_nice && cpucolors="$cpucolors rgbi:0.6/1.0/0.7" +$have_intr && cpucolors="$cpucolors yellow2" +$have_wait && cpucolors="$cpucolors cyan2" +cpucolors="$cpucolors )" + +if [ -z "$titleArg" ] +then + titleArg="SGI PCP : High-Level Activity for Host $host" +fi + +cat << end-of-file >> $tmp.conf + +_colorlist cpu_colors $cpucolors +_colorlist disk_colors ( violet yellow ) +_colorlist ctrl_colors ( green2 ) +_colorlist network_colors ( green2 blue2 red2 ) + +_colorlist memory_colors ( + rgbi:1.0/1.0/0.0 + rgbi:0.0/1.0/1.0 + rgbi:1.0/0.0/0.0 + rgbi:1.0/0.0/1.0 + rgbi:0.0/0.0/1.0 + rgbi:0.0/1.0/0.0 +) + +_grid hide ( + +# +# System level stuff +# +end-of-file + +if [ "$ndisk" -gt 0 ] +then + cat << end-of-file >> $tmp.conf + + _label 0 1 _down _large "Disk" + + _stack 1 1 _west _cylinder ( + _metrics ( + disk.all.write $maxio + disk.all.read $maxio + ) + _colorlist disk_colors + _baseLabel "Disk Operations\nNormalized to $maxio I/Os per second" + ) +end-of-file + + if $allAvg + then + cat << end-of-file >> $tmp.conf + + _bar 2 1 _west _cylinder ( + _metrics ( + disk.all.avg_disk.active $maxdiskscale + ) + _colorlist ctrl_colors + _baseLabel "Disk Activity\nNormalized to ${maxdisk}% for $ndisk disks" + ) +end-of-file + fi + + if [ "$nctrl" -gt 0 ] + then + cat << end-of-file >> $tmp.conf + + _label 4 0 _west "Disk Controllers" + + _bar 4 1 _west _cylinder ( + _metrics ( +end-of-file + + cat $tmp.ctrls | tr '\012' ' ' \ + | $PCP_AWK_PROG -v cols=$ctrlcols -v scale=$maxdiskscale >> $tmp.conf ' +BEGIN { str = ""; j = 0 } + { for (i = 1; i <= NF; i++) { + if (str == "") + str = $i; + else + str = str "," $i + if (j == cols) { + printf(" disk.ctl.avg_disk.active[%s] %d\n", str, scale); + str = ""; + j = 0; + } + else + j++; + } + } +END { if (str != "") + printf(" disk.ctl.avg_disk.active[%s] %d\n", str, scale); + }' + + cat << end-of-file >> $tmp.conf + ) + _colorlist ctrl_colors + _baseLabel "Disk Controller Activity\nNormalized to ${maxdisk}% for the disks on each controller" + ) +end-of-file + + fi +fi + +cat << end-of-file >> $tmp.conf + + _label 0 3 _west _down _large "Load" + + _bar 1 3 2 1 _west ( + _metrics ( + kernel.all.load[15] $maxload + kernel.all.load[5] $maxload + kernel.all.load[1] $maxload + ) + _metriclabels _away ( "15" "5" "1" ) + _colorlist ( blue2 blue2 blue2 ) + _baseLabel "Average System Load over the last 1, 5, and 15 minutes\nNormalized to $maxload" + ) + + _label 0 5 _west _down _large "Mem" + +end-of-file + +if [ $realmem -ne 0 ] +then + if $utilmem + then + cat << end-of-file >> $tmp.conf + _stack 1 5 _west ( + _metrics ( + mem.util.kernel $realmem + mem.util.fs_ctl $realmem + mem.util.fs_dirty $realmem + mem.util.fs_clean $realmem + mem.util.user $realmem + ) + _colorlist memory_colors + _baseLabel "Physical Memory Utilization\nNormalized to $realmem Kbytes" + ) +end-of-file + elif $linuxutilmem + then + cat << end-of-file >> $tmp.conf + _stack 1 5 _west ( + _metrics ( + mem.util.shared $realmem + mem.util.cached $realmem + mem.util.bufmem $realmem + mem.util.other $realmem + mem.util.free $realmem + ) + _colorlist memory_colors + _baseLabel "Physical Memory Utilization\nNormalized to `expr $realmem / 1024` Kbytes" + ) +end-of-file + else + cat << end-of-file >> $tmp.conf + _stack 1 5 _west _fillmod ( + _metrics ( + mem.freemem $realmem + ) + _colorlist ( blue2 ) + _baseLabel "Unallocated Physical Memory" + _stackLabel "Used Physical Memory\nNormalized to $realmem Kbytes" + ) +end-of-file +fi +elif $utilmem +then + cat << end-of-file >> $tmp.conf + _stack 1 5 _west _utilmod ( + _metrics ( + mem.util.kernel 0 + mem.util.fs_ctl 0 + mem.util.fs_dirty 0 + mem.util.fs_clean 0 + mem.util.user 0 + mem.util.free 0 + ) + _colorlist memory_colors + _baseLabel "Physical Memory Utilization" + ) +end-of-file +elif $linuxutilmem +then + cat << end-of-file >> $tmp.conf + _stack 1 5 _west _utilmod ( + _metrics ( + mem.util.shared 0 + mem.util.cached 0 + mem.util.bufmem 0 + mem.util.other 0 + mem.util.free 0 + ) + _colorlist memory_colors + _baseLabel "Physical Memory Utilization" + ) +end-of-file +fi + +cat << end-of-file >> $tmp.conf + + _label 0 7 _west _down _large "CPU" + + _stack 1 7 _west ( + _metrics ( +end-of-file +$have_user && echo " kernel.all.cpu.user $maxcpu" >> $tmp.conf +$have_sys && echo " kernel.all.cpu.sys $maxcpu" >> $tmp.conf +$have_nice && echo " kernel.all.cpu.nice $maxcpu" >> $tmp.conf +$have_intr && echo " kernel.all.cpu.intr $maxcpu" >> $tmp.conf +$have_wait && echo " kernel.all.cpu.wait.total $maxcpu" >> $tmp.conf +cat << end-of-file >> $tmp.conf + ) + _colorlist cpu_colors + _baseLabel "CPU Utilization\nSummed over $cpudesc" + ) +end-of-file + +if [ "$ninterfaces" -gt 0 ] +then + cat << end-of-file >> $tmp.conf + +# +# Network Stuff and Alarms +# + + _marginWidth 1 + + _grid 4 2 1 7 _nw ( + + _marginWidth 8 + + _label 0 0 _sw "Network Input" + + _bar 0 1 _nw _groupByMetric ( + _metrics ( + network.interface.in.bytes[$interfaces] $maxbyte + network.interface.in.packets[$interfaces] $maxpack + network.interface.in.errors[$interfaces] $maxerr + ) + _colorlist network_colors + _metricLabels _away ( "Bytes" "Packets" "Errors" ) + _baseLabel "Input on all Network Interfaces\nNormalized to $maxbyte bytes per second and $maxpack packets per second" + ) + + _label 0 2 _sw "Network Output" + + _bar 0 3 _nw ( + _metrics ( + network.interface.out.bytes[$interfaces] $maxbyte + network.interface.out.packets[$interfaces] $maxpack + network.interface.out.errors[$interfaces] $maxerr + ) + _instlabels _away ( $interfaces_sp ) + _metricLabels _away ( "Bytes" "Packets" "Errors" ) + _colorlist network_colors + _baseLabel "Output on all Network Interfaces\nNormalized to $maxbyte bytes per second and $maxpack packets per second" + ) + ) +end-of-file +fi + +echo ")" >> $tmp.conf + +$verbose && cat $tmp.conf + +eval $PMVIEW <$tmp.conf $args -title "'$titleArg'" -xrm "'*iconName:osvis'" -R $PCP_VAR_DIR/config/pmlogger/config.osvis + +exit diff --git a/src/pmview/front-ends/pmview-args b/src/pmview/front-ends/pmview-args new file mode 100644 index 0000000..31a7008 --- /dev/null +++ b/src/pmview/front-ends/pmview-args @@ -0,0 +1,625 @@ +# Copyright (c) 1995-2005 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +prog=`basename $0` + +PMVIEW=pmview +PMPROBE=pmprobe +CONFIRM="$PCP_XCONFIRM_PROG" +ECHONL="echo -n" +_multiple_sources=false + +# +# Standard usage and command-line argument parsing for pmview front ends. +# This file should be included by pmview front end scripts to present a +# consistent interface. See pmview(1), dkvis(1), mpvis(1) and nfsvis(1) +# for more information on their respective interfaces. +# + +# +# The front end scripts should call _pmview_usage after their own usage +# information in a subroutine called _usage. The _usage subroutine may be +# called by either _pmview_note or _pmview_args. +# +_pmview_usage() +{ + if $_multiple_sources + then + echo -n ' + -A align align sample times on natural boundaries + -a archive[,archive...] + metrics source is one or more PCP archive logs + -C check configuration file and exit + -h host[,host...] metrics source is PMCD on one or more hosts' + + else + + echo -n ' + -A align align sample times on natural boundaries + -a archive metrics source is a PCP archive log + -C check configuration file and exit + -h host metrics source is PMCD on host' + + fi + + echo ' + -n pmnsfile use an alternative PMNS + -O offset initial offset into the time window + -p port port name for connection to existing time control + -S starttime start of the time window + -T endtime end of the time window + -t interval sample interval [default 2.0 seconds] + -x version use pmlaunch(5) version [default version 2.0] + -z set reporting timezone to local time of metrics source + -Z timezone set reporting timezone + + -display display-string + -geometry geometry-string + -name name-string + -title title-string + -xrm resource' +} + +# Most front-end scripts can handle input from only one source, and +# hence only one -h or -a option. +# +# For those than can handle multiple sources, they should call +# _pmview_multiple_sources_are_ok before calling _pmview_args to +# enable the following extensions to the -a and -h options: +# -a a1 -a a2 ... multiple -a options +# -h h1 -h h2 ... multiple -h options +# -a a1,a2,... multiple archives with the -a option +# -h h1,h2,... multiple hosts with the -h option +# +_pmview_multiple_sources_are_ok() +{ + _multiple_sources=true +} + +# check for magic numbers in a file that indicate it is a PCP archive +# +# if file(1) was reliable, this would be much easier, ... sigh +# +# if you need to change this, make consistent changes in all these +# places: +# _check_file() in src/pmafm/mkaf +# _is_archive() in src/pmafm/pmafm +# _is_archive() in src/pmview/front-ends/pmview-args +# +_is_archive() +{ + case "$PCP_PLATFORM" in + irix) + dd ibs=1 count=7 if="$1" 2>/dev/null | od -X | $PCP_AWK_PROG ' +NR == 1 && $2 == "00000084" && $3 == "50052600" { exit 0 } + { exit 1 }' + ;; + linux) + dd ibs=1 count=7 if="$1" 2>/dev/null | od -x | $PCP_AWK_PROG ' +NR == 1 && NF == 5 && $2 == "0000" && $3 == "0084" && $4 == "5005" && $5 == "2600" { exit 0 } +NR == 1 && NF == 5 && $2 == "0000" && $3 == "8400" && $4 == "0550" && $5 == "0026" { exit 0 } + + { exit 1 }' + ;; + esac + return $? +} + +# One of the first actions of a front end script should be to call +# _pmview_args. It sets the following variables: +# +# host the first host specified with -h (if any) in the format +# hostname +# else the host from the first archive in the format +# hostname (Archive archivename) +# arch the first archive specified with -a (if any). +# All archives are passed on to pmview with one -a +# argument per archive via $args +# numsource Number of metrics sources (hosts or archives) +# args The list of args that pmview will comprehend and use. +# otherArgs The arguments pmview will not understand and should be +# handled by the front end script. +# titleArg The title the user prefers. If empty, the title should be +# provided by the front end script. +# prog The name of the program. +# namespace The namespace (including the flag) if specified, else empty +# eg "-n foo" +# msource The default metrics source, whether live or an archive, +# including the flag. e.g. "-h blah" or "-a first_archive". +# Taken from the first encountered -a or -h option. +# +# sourcelist space separated list of hosts or archives +# +_pmview_args() +{ + +_seen_host=false +_seen_arch=false +host="" +arch="" +numsource=0 +args="" +otherArgs="" +titleArg="" +namespace="" +msource="" + +if [ $# -eq 1 -a '$1' = '-\?' ] +then + _usage + exit 0 +fi + +while [ $# -gt 0 ] +do + case $1 + in + -g*|-di*|-name|-xrm) + # assume an X11 argument + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: X-11 option $1 requires one argument" + #NOTREACHED + fi + args="$args $1 '$2'" + shift + ;; + + -title) + # assume an X11 argument + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + exit 1 + fi + titleArg="$2" + shift + ;; + + -A|-D|-O|-p|-S|-T|-t|-x|-Z) + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + args="$args $1 '$2'" + shift + ;; + + -A*|-D*|-O*|-p*|-S*|-T*|-t*|-Z*|-C|-z) + args="$args $1" + ;; + + -a) + if $_seen_host + then + _pmview_note Usage-Error error "$prog: Only one of the -h or -a options may be specified" + #NOTREACHED + fi + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + for _archbase in `echo "$2" | sed -e 's/,/ /g'` + do + if [ $numsource -gt 0 -a $_multiple_sources = false ] + then + _pmview_note Usage-Error error "$prog: Only one -a option may be specified" + # NOTREACHED + fi + if _is_archive $_archbase 2>&1 + then + _archbase=`echo $_archbase | sed -e 's/\.[^.]*$//'` + fi + # at least $_archbase.0 and $_archbase.meta have to be here + # + if _is_archive $_archbase.0 && _is_archive $_archbase.meta + then + : + else + _pmview_note Error error "$prog: \"$_archbase\" is not the basename of a valid PCP archive" + #NOTREACHED + fi + if [ -z "$arch" ] + then + # first archive seen + # + arch=$_archbase + msource="-a $_archbase" + host=`pmdumplog -l $arch \ + | $PCP_AWK_PROG '/^Performance/ {print $5}' \ + | sed -e 's/,//g'` + [ "X$host" = X ] && host="unknown host" + host="$host (Archive $arch)" + fi + + # pmview(1) can handle multiple -a options, so + # pass _all_ archive names back to the caller both as + # -a options via $args and in $sourcelist (counted by + # $numsource). + # + # Note: multiple -h options are handled slightly + # differently, see also the comments for -h below. + # + + args="$args -a $_archbase" + if [ -z "$sourcelist" ]; then + sourcelist=$_archbase + else + sourcelist="$sourcelist $_archbase" + fi + numsource=`expr $numsource + 1` + done + _seen_arch=true + shift + ;; + + -h) + if $_seen_arch + then + _pmview_note Usage-Error error "$prog: Only one of the -h or -a options may be specified" + #NOTREACHED + fi + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + for _host in `echo "$2" | sed -e 's/,/ /g'` + do + if [ $numsource -gt 0 -a $_multiple_sources = false ] + then + _pmview_note Usage-Error error "$prog: Only one -h option may be specified" + # NOTREACHED + fi + if [ -z "$host" ] + then + host=$_host + msource="-h $_host" + + # pmview(1) can handle only one -h options, so + # pass the _first_ host name back to the caller as a + # -h option via $args and _all_ hostnames via + # $sourcelist (counted by $numsource). + # + # Note: multiple -a options are handled slightly + # differently, see also the comments for -a above. + # + + args="$args -h $_host" + fi + if [ -z "$sourcelist" ]; then + sourcelist=$_host + else + sourcelist="$sourcelist $_host" + fi + numsource=`expr $numsource + 1` + done + _seen_host=true + shift + ;; + + -n) + if [ $# -lt 2 ] + then + _pmview_note Usage-Error error "$prog: Option $1 requires one argument" + #NOTREACHED + fi + namespace="-n $2" + args="$args -n $2" + shift + ;; + + *) + otherArgs="$otherArgs $1" + ;; + + esac + shift + +done + +if [ -z "$host" ] +then + host=`pmhostname` + msource="-h $host" +fi +} + +# standard fatal error reporting +# Usage: _pmview_error message goes in here +# _pmview_error -f file +# +_pmview_error() +{ + _pmview_note Error error $* +} + +# standard warning +# Usage: _pmview_warning message goes in here +# _pmview_warning -f file +# +_pmview_warning() +{ + _pmview_note Warning warning $* +} + +# standard info +# Usage: _pmview_info message goes in here +# _pmview_info -f file +# +_pmview_info() +{ + _pmview_note Info info $* +} + +# generic notifier +# Usage: _pmview_note tag icon args ... +# +_pmview_note() +{ + tag=$1; shift + icon=$1; shift + button="" + [ $tag = Error ] && button="-B Quit" + [ $tag = Usage-Error ] && button="-B Quit -b Usage" + + [ X"$PCP_STDERR" = XDISPLAY -a -z "$DISPLAY" ] && unset PCP_STDERR + + if [ $# -eq 2 -a "X$1" = X-f ] + then + case "$PCP_STDERR" + in + DISPLAY) + ans=`$CONFIRM -icon $icon -file $2 -useslider -header "$tag $prog" $button 2>&1` + ;; + '') + echo "$tag:" >&2 + cat $2 >&2 + ans=Quit + ;; + *) + echo "$tag:" >>$PCP_STDERR + cat $2 >>$PCP_STDERR + ans=Quit + ;; + esac + else + case "$PCP_STDERR" + in + DISPLAY) + ans=`$CONFIRM -icon $icon -t "$*" -noframe -header "$tag $prog" $button 2>&1` + ;; + '') + echo "$tag: $*" >&2 + ans=Quit + ;; + *) + echo "$tag: $*" >>$PCP_STDERR + ans=Quit + ;; + esac + fi + + if [ $tag = Usage-Error ] + then + [ $ans = Usage ] && _usage + tag=Error + fi + + [ $tag = Error ] && exit 1 +} + +# used internally by _pmview_cache_fetch() and _pmview_fetch() +# +_pmview_probe() +{ + flag=$1 + shift + ( echo $* \ + ; echo "-----" \ + ; ( $PMPROBE $namespace $msource $flag $* 2>$tmp.pmview_err \ + | tee -a $tmp.pmview_fetch \ + ) \ + ) \ + | tr ' ' '\012' \ + | sed -e 's/"//g' \ + | $PCP_AWK_PROG ' +BEGIN { last = 0 } +$1 == "-----" { state = 1; next } +state == 0 { metric[last] = pat[last] = $1 + # deal with arguments that are non-terminals in the PMNS + # so pmprobe a.b => a.b.c a.b.c.x a.b.d etc + gsub("\\.", "\\.", pat[last]) + pat[last] = "^" pat[last] "\\." + last++ + next + } +state > 0 { for (i = 0; i < last; i++) { + if (metric[i] == $1 || match($1, pat[i]) > 0) { + # new matching metric name + name=$1 + state=2 + next + } + } + } +state == 2 { if ($1 > 0) state = 3 + else state = 1 + next + } +state == 3 { printf("%s%s|%s\n", "'"$flag|$msource|"'",name,$1) }' +} + +# Fetch metrics and cache result +# input +# $1 - pmprobe flag +# $* - 1 or more metrics +# output +# $tmp.pmview_cache - cached values, with this format +# pmprobe flag|metric source|metric name|pmprobe result +# +_pmview_cache_fetch() +{ + flag=$1 + shift + _pmview_probe $flag $* >>$tmp.pmview_cache + + return 0 +} + +# Fetch metrics +# +# input +# $1 - pmprobe flag +# $2 - metric name +# output +# $number - number of values +# $tmp.pmview_result - values +# +_pmview_fetch() +{ + flag=$1 + metric=$2 + rm -f $tmp.pmview_fetch $tmp.pmview_result + if [ -s $tmp.pmview_cache ] + then + $PCP_AWK_PROG -F\| \ + <$tmp.pmview_cache >$tmp.pmview_result \ +' +$1 == "'"$flag"'" && $2 == "'"$msource"'" && $3 == "'"$metric"'" { print $4 }' + fi + + if [ ! -s $tmp.pmview_result ] + then + # cache miss, forced to probe + # + _pmview_probe $flag $metric \ + | $PCP_AWK_PROG -F\| >$tmp.pmview_result '{print $4}' + fi + + if [ -s $tmp.pmview_result ] + then + number=`wc -l <$tmp.pmview_result | sed -e 's/ //g'` + else + if [ -s $tmp.pmview_fetch ] + then + check=`cut -d ' ' -f 2 $tmp.pmview_fetch` + if [ "$check" = "$metric" ] + then + # *.pmview_fetch looks valid, extract numval from 2nd field + # + number=`cut -d ' ' -f 2 $tmp.pmview_fetch` + else + # *.pmview_fetch exists, but does not contain + # pmprobe output, more than likely this is some + # sort of fatal error message ... but _real_ message + # is likely to be in *.pmview_err + # + [ -s $tmp.pmview_err ] && mv $tmp.pmview_err $tmp.pmview_fetch + number=-1 + fi + else + number=-1 + mv $tmp.pmview_err $tmp.pmview_fetch + fi + fi + if [ $number -le 0 ] + then + rm -f $tmp.pmview_result + return 1 + fi + + return 0 +} + +# Fetch the metric values +# +# input +# $1 - metric name +# output +# $number - number of values +# $tmp.pmview_result - values +# +_pmview_fetch_values() +{ + _pmview_fetch -v $1 + return $? +} + +# Fetch the metric instance list +# +# input +# $1 - metric name +# output +# $number - number of instances +# $tmp.pmview_result - instances +# +_pmview_fetch_indom() +{ + _pmview_fetch -I $1 + return $? +} + +# Convert pmprobe/pminfo error message into something useful and +# consistent +# +_pmview_fetch_mesg() +{ + $PCP_AWK_PROG ' +$1 == "pmprobe:" { $1 = "'$prog':"; print; exit } +$1 == "pminfo:" { $1 = "'$prog':"; print; exit } +$1 == "Error:" { $1 = ":"; + printf("%s: %s%s\n", "'$prog'", metric, $0); exit } +$1 == "inst" { exit } +NF == 1 { metric = $1; next } +NF == 0 { next } +NF == 2 && $2 == "0" { printf("%s: %s: No values available\n", "'$prog'", $1); exit} + { $2 = ":"; print "'$prog': " $0; exit}' \ + | sed "s/ : /: /" \ + | fmt +} + +# Generate error metric for failed fetch +# +_pmview_fetch_fail() +{ + cat $tmp.pmview_fetch | _pmview_fetch_mesg >> $tmp.msg + echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp.msg + _pmview_error -f $tmp.msg + # NOTREACHED +} + +# Generate warning message for failed fetch +# +_pmview_fetch_warn() +{ + cat $tmp.pmview_fetch | _pmview_fetch_mesg >> $tmp.msg + echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp.msg + _pmview_warning -f $tmp.msg + rm -f $tmp.msg +} + +# Check that $OPTARG for option $1 is a positive integer +# ...note the creative use of unary - to prevent leading signs +# +_pmview_unsigned() +{ + if [ "X-$OPTARG" != "X`expr 0 + -$OPTARG 2>/dev/null`" ] + then + _pmview_error "$prog: -$1 option must have a positive integral argument" + # NOTREACHED + fi +} diff --git a/src/pmview/front-ends/weblogvis b/src/pmview/front-ends/weblogvis new file mode 100755 index 0000000..5b570bd --- /dev/null +++ b/src/pmview/front-ends/weblogvis @@ -0,0 +1,451 @@ +#!/bin/sh +# Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +# default idle time +# +defidle=3600 + +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] [server ...] + +Options + -b display byte rate, rather than request rate + -f display activity by function, rather than activity by + result size + -i show server names + -I time maximum expected idle time in seconds [default $defidle] + -m max maximum expected request rate (or maximum byte rate) + -V verbose/diagnostic output + +pmview(1) options:' + _pmview_usage >> $tmp.msg + echo ' +Default title is: Web Log Activity (request rate by result size) for Host' >> $tmp.msg + echo ' + The default is to display the request rate, grouped by result size, for all + Web servers on the target host.' | fmt >> $tmp.msg + _pmview_info -f $tmp.msg +} + +# --- build WM_COMMAND X(1) property for restart after login/logout --- +# +echo -n "pmview Version 2.1 \"$prog\"" >$tmp.pmview +for arg +do + echo -n " \"$arg\"" >>$tmp.pmview +done +echo >> $tmp.pmview + +# --- parse command line arguments --- +# +verbose=false +serverList="" +max=0 +idle=$defidle +class=size +show=request +showInst=false + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?bfiI:m:v:V" c $otherArgs + do + case $c + in + b) # show bytes not requests + show=byte + ;; + + f) # classify by function (method) + class=method + ;; + i) + showInst=true + ;; + I) + idle=$OPTARG + # and now the obscure +ve integer checking bit + # ...note the creative use of unary - to prevent leading signs + if [ "X-$idle" != "X`expr 0 + -$idle 2>/dev/null`" ] + then + _pmview_error "$prog: -I must have a positive integral argument" + #NOTREACHED + fi + ;; + m) + max=$OPTARG + # and now the obscure +ve integer checking bit + # ...note the creative use of unary - to prevent leading signs + if [ "X-$max" != "X`expr 0 + -$max 2>/dev/null`" ] + then + _pmview_error "$prog: -m must have a positive integral argument" + #NOTREACHED + fi + ;; + v) + if [ $OPTARG = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + elif [ $OPTARG != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + V) + verbose=true + ;; + '?') + _usage + exit 1 + ;; + esac + done + + set - $otherArgs + shift `expr $OPTIND - 1` + [ $# -gt 0 ] && serverList_sp=$* && serverList=`echo $* | sed -e 's/ /,/g'` +fi +[ -z "$titleArg" ] && titleArg="SGI PCP : Web Log Activity" + + +if [ $max = 0 ] +then + if [ $show = request ] + then + # requests per second + max=50 + else + # bytes per second + max=500000 + fi +fi + + +# maximum req error rate (default: 5% of request rate) +# +maxerr=`expr $max / 20` + + +# if metrics source is an archive, and find out name of host +# +if [ "X$arch" != X ] +then + host=`pmdumplog -l $arch | $PCP_AWK_PROG '/^Performance/ {print $5}' \ + | sed -e 's/,//g'` + [ "X$host" = X ] && host="unknown host" + host="$host (Archive $arch)" +fi + + +# get perserver instances +# in the default case use web.perserver.requests.total +# in the -b flag case use web.perserver.bytes.total +# +rm -f $tmp.int +if [ $show = request ] +then + pminfo -f $msource $namespace web.perserver.requests.total \ + > $tmp.int 2>/dev/null +else + pminfo -f $msource $namespace web.perserver.bytes.total \ + > $tmp.int 2>/dev/null +fi +if [ ! -s $tmp.int ] +then + if [ "X$arch" != "X" ] + then + _pmview_error "$prog: weblog metrics not included in the archive \"$arch\"" + #NOTREACHED + else + _pmview_error "$prog: weblog metrics not available for host \"$host\"" + #NOTREACHED + fi +fi + +sed -e 's/\]//' -e 's/"//g' $tmp.int \ + | $PCP_AWK_PROG '$1 == "inst" {print $4}' > $tmp.list + +if [ ! -s $tmp.list ] +then + if [ "X$arch" != X ] + then + _pmview_error "$prog: failed to get web servers from archive \"$arch\"" + #NOTREACHED + else + _pmview_error "$prog: failed to get web servers from host \"$host\"" + #NOTREACHED + fi +fi + +rm -f $tmp.chosen +if [ -z "$serverList" ] +then + cp $tmp.list $tmp.chosen +else + touch $tmp.chosen + for i in $serverList_sp + do + egrep $i $tmp.list >> $tmp.chosen + done +fi + +servers_sp=`cat $tmp.chosen | sort | uniq` +servers=`echo $servers_sp | sed -e 's/ /,/g'` +nservers=`echo $servers | wc -w` + +if [ $nservers = 0 ] +then + _usage + echo + echo "Error: $prog: no matching web servers" + echo " Available web servers are: " `cat $tmp.list` + exit 1 +fi + + +if [ $show = request ] +then + totalLabel="Total_Req_Rate" + label_sp="\"Request Rate\"" + met="requests" + titleArg="$titleArg (request rate" +else + totalLabel="Total_Data_Rate" + label_sp="\"Data Rate\"" + met="bytes" + titleArg="$titleArg (data rate" +fi + +# +# strings for base plane labels +# +basestr_idle="Elapsed time since the last request\nNormalized to $idle seconds" +if [ $show = request ] +then + basestr_total="Total number of HTTP requests processed by server\nNormalized to $max requests per second" + basestr_req="HTTP request rate by response size in bytes\nNormalized to $max hits per second" + basestr_type="HTTP request rate by HTTP method\nNormalized to $max hits per second" +else + basestr_total="Total number of bytes processed by server\nNormalized to $max bytes per second" + basestr_req="HTTP size rate by response size in bytes\nNormalized to $max bytes per second" + basestr_type="HTTP size rate by HTTP method\nNormalized to $max hits per second" +fi + + +#--- config file has already been created; continue writing to it --- +# + +cat << end-of-file >>$tmp.pmview +# +# $prog +# +# Servers: $servers +# +end-of-file + +if $verbose +then + if [ "X$serverList" != X ] + then + echo "# Matching servers for \"$serverList\": $servers" + fi +fi + +if [ $class = size ] +then + titleArg="$titleArg by result size)" +else + titleArg="$titleArg by request type)" +fi + + +# +# the real config starts here +# +cat << end-of-file >>$tmp.pmview + +_scale 1.2 + +_grid _hide ( + + _label 2 0 1 5 _down _large $label_sp + + _bar 0 0 east _col ( + _metrics ( + web.perserver.logidletime[$servers] $idle + ) + _metriclabels ( Idle ) + _colorList ( orange ) + _baseLabel "$basestr_idle" + ) + +end-of-file + +if [ $class = size ] +then + cat << end-of-file >>$tmp.pmview + + _bar 0 2 east _col ( + _metrics ( + web.perserver.$met.total[$servers] $max + ) + _metriclabels ( Total ) + _colorList ( rgbi:0.0/0.88/0.88 ) + _baseLabel "$basestr_total" + ) + + _bar 0 4 east _col _groupbyinst ( + _metrics ( +end-of-file + + if [ $show = request ] + then + cat <<end-of-file >>$tmp.pmview + web.perserver.$met.size.unknown[$servers] $max +end-of-file + fi + + cat << end-of-file >>$tmp.pmview + web.perserver.$met.size.gt3m[$servers] $max + web.perserver.$met.size.le3m[$servers] $max + web.perserver.$met.size.le1m[$servers] $max + web.perserver.$met.size.le300k[$servers] $max + web.perserver.$met.size.le100k[$servers] $max + web.perserver.$met.size.le30k[$servers] $max + web.perserver.$met.size.le10k[$servers] $max + web.perserver.$met.size.le3k[$servers] $max +end-of-file + + if [ $show = request ] + then + cat <<end-of-file >>$tmp.pmview + web.perserver.$met.size.zero[$servers] $max + ) + _metriclabels ( + "?" "> 3M" "1M < 3M" "300K < 1M" "100K < 300K" + "30K < 100K" "10K < 30K" "3K < 10K" "0 < 3K" "0K" + ) +end-of-file + else + cat <<end-of-file >>$tmp.pmview + ) + _metriclabels ( + "> 3M" "1M < 3M" "300K < 1M" "100K < 300K" + "30K < 100K" "10K < 30K" "3K < 10K" "0 < 3K" + ) +end-of-file + fi + + if $showInst + then + cat <<end-of-file >>$tmp.pmview + _instlabels _away ( $servers_sp ) +end-of-file + fi + + cat << end-of-file >>$tmp.pmview + _colorList ( + rgbi:1.0/0.35/0.0 + rgbi:0.6/0.0/0.9 + rgbi:0.0/1.0/0.0 + rgbi:1.0/0.5/0.0 + rgbi:0.65/0.3/1.0 + rgbi:0.3/1.0/0.3 + rgbi:1.0/0.65/0.3 + rgbi:0.8/0.6/1.0 + rgbi:0.6/1.0/0.6 + rgbi:1.0/0.8/0.6 + + ) + _baseLabel "$basestr_req" + ) + +end-of-file + +else + cat << end-of-file >>$tmp.pmview + + _bar 0 2 east _col ( + _metrics ( + web.perserver.$met.total[$servers] $max + ) + _metriclabels ( Total ) + _colorList ( rgbi:1.0/0.5/0.0 ) + _baseLabel "$basestr_total" + ) + + _bar 0 4 east _col ( + _metrics ( + web.perserver.$met.get[$servers] $max + web.perserver.$met.head[$servers] $max + web.perserver.$met.post[$servers] $max + web.perserver.$met.other[$servers] $max +end-of-file + + if [ $show = request ] + then + cat << end-of-file >>$tmp.pmview + web.perserver.errors[$servers] $maxerr +end-of-file + fi + + cat << end-of-file >>$tmp.pmview + ) + _metriclabels ( Get Head Post Other Error ) +end-of-file + + if $showInst + then + cat <<end-of-file >>$tmp.pmview + _instlabels _away ( $servers_sp ) +end-of-file + fi + + cat << end-of-file >>$tmp.pmview + _colorList ( + rgbi:1.0/1.0/0.0 + rgbi:0.0/1.0/1.0 + rgbi:1.0/0.0/1.0 + rgbi:1.0/1.0/0.6 + rgbi:0.8/0.0/0.0 + ) + _baseLabel "$basestr_type" + ) +end-of-file + +fi + +cat << end-of-file >>$tmp.pmview +) +end-of-file + +titleArg="$titleArg for Host $host" + +$verbose && cat $tmp.pmview + +eval $PMVIEW <$tmp.pmview $args -title "'$titleArg'" -xrm "'*iconName: $prog'" + +exit + diff --git a/src/pmview/front-ends/weblogvis.load b/src/pmview/front-ends/weblogvis.load new file mode 100755 index 0000000..a676124 --- /dev/null +++ b/src/pmview/front-ends/weblogvis.load @@ -0,0 +1,103 @@ +#!/bin/sh +# Copyright (c) 2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Load up a Web server to show off/QA weblogvis +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; killall -9 load; exit" 0 1 2 3 15 + +i=0 + +# specular +# +cat >$tmp.$i <<End-of-File +GET http://155.11.225.108/file8k.html +GET http://155.11.225.108/file15k.html +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +cat >$tmp.$i <<End-of-File +GET http://155.11.225.108/file15k.html +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +# specular IP alias +# +cat >$tmp.$i <<End-of-File +GET http://155.11.225.118/file45k.html +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +cat >$tmp.$i <<End-of-File +GET http://155.11.225.118/file15k.html +GET http://155.11.225.118/file45k.html +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +cat >$tmp.$i <<End-of-File +GET http://155.11.225.118/file45k.html +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +# specular proxy +# +cat >$tmp.$i <<End-of-File +GET http://boing/file15k.html +End-of-File +webping -c $tmp.$i -I0 -q -P specular:8080 & +i=`expr $i + 1` + +cat >$tmp.$i <<End-of-File +GET http://boing/file0k.html +GET http://boing/file0k.html +GET http://boing/file115k.html +End-of-File +webping -c $tmp.$i -I0 -q -P specular:8080 & +i=`expr $i + 1` + +# specular socks +# +cat >$tmp.$i <<End-of-File +GET http://155.11.225.108/file500k.html +GET http://boing/file500k.html +End-of-File +webping -c $tmp.$i -I0 -q -S specular:8080 & +i=`expr $i + 1` + +# specular ftp-socks +# +cat >$tmp.$i <<End-of-File +GET ftp://155.11.225.108/pub/README +End-of-File +webping -c $tmp.$i -I0 -q -S specular:8080 & +i=`expr $i + 1` + +# specular ftp +# +cat >$tmp.$i <<End-of-File +GET ftp://155.11.225.108/pub/README +End-of-File +webping -c $tmp.$i -I0 -q & +i=`expr $i + 1` + +wait diff --git a/src/pmview/front-ends/weblogvis.rgb b/src/pmview/front-ends/weblogvis.rgb Binary files differnew file mode 100644 index 0000000..11c4712 --- /dev/null +++ b/src/pmview/front-ends/weblogvis.rgb diff --git a/src/pmview/front-ends/webpingvis b/src/pmview/front-ends/webpingvis new file mode 100644 index 0000000..a08aa81 --- /dev/null +++ b/src/pmview/front-ends/webpingvis @@ -0,0 +1,372 @@ +#! /bin/sh +# Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] + +options: + -i show URL names + -m max maximum expected total ping response time (milliseconds) + -V verbose/diagnostic output + +pmview(1) options:' + + _pmview_usage >> $tmp.msg + echo >> $tmp.msg + echo 'Default title is: SGI PCP: Web PING Performance for Host' >> $tmp.msg + echo ' + The "max" parameter (used with -m) should be set to the maximum + expected ping response time for all URLs being monitored. The default + is 1000 milliseconds. This governs the height of the bar showing total + response time (at the far right of the scene). All bars showing + response times use "max" for height normalization.' >> $tmp.msg + _pmview_info -f $tmp.msg +} + +#--- build WM_COMMAND X(1) property for restart after login/logout --- +# +echo -n "pmview Version 2.1 \"$prog\"" > $tmp.pmview +for arg +do + echo -n " \"$arg\"" >> $tmp.pmview +done +echo >> $tmp.pmview + +# --- LOCAL FUNCTIONS : START ------------------------------------------------ +_sort_host_indom() +{ + sed <$1 \ + -e 's/instance\[\([0-9][0-9]*\)]:/\1/' \ + | $PCP_AWK_PROG ' +BEGIN { state = 0 } +/^samples:/ { state = 1; next } +state == 0 { next } +/^full label/ { label[$4] = $NF; next } +NF == 0 { state = 2; next } +state == 2 { for (i=1; i<=NF; i++) { + if (label[i-1] == "") + label[i-1] = $i + } + state = 3 + next + } +state == 3 { for (i=1; i<=NF; i++) + size[i-1] = $i + } +END { for (i in label) { + print label[i],size[i] + } + }' \ + | sort +1n -2 +0 -1 >$tmp.tmp + mv $tmp.tmp $1 +} + +_sort_arch_indom() +{ +metric=webping.perurl.kbytes +indom=`pminfo -d -a $arch $metric \ +| sed -n -e '/InDom:/{ +s/.*InDom: // +s/ .*//p +}'` + +if [ "X$indom" = XPM_INDOM_NULL ] +then + _pmview_error "$prog: $metric is singular" + #NOTREACHED +elif [ -z "$indom" ] +then + _pmview_error "$prog: cannot determine InDom for $metric" + #NOTREACHED +fi + +pmdumplog -z -i $arch 2>&1 \ +| $PCP_AWK_PROG ' +/^InDom: '$indom'$/ { state=1; next } +/^InDom: / { state=0; next } +state == 1 && /^[^ ]/ { print }' \ +| while read stamp junk +do + pminfo -f -a $arch -z -O "@$stamp" -f $metric +done \ +| sed -n \ + -e '/ value /{ +s/"] value// +s/.*"//p +}' \ +| sort -u >$tmp.known + +pmdumplog -z -i $arch 2>&1 \ +| $PCP_AWK_PROG ' +/^InDom: '$indom'$/ { state=1; next } +/^InDom: / { state=0; next } +state == 1 && /^[ ]/ { print }' \ +| sed -n \ + -e '/ or /{ +s/.* or "// +s/".*//p +}' \ +| sort -u \ +| join -a1 - $tmp.known \ +| sort +2n -3 +1 -2 \ +| $PCP_AWK_PROG ' +NF==1 { printf("%s ?\n",$0); next } + { print }' > $1 +} +# --- LOCAL FUNCTIONS : END -------------------------------------------------- + +verbose=false +max=1000 +showInst=false + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?im:v:V" c $otherArgs + do + case $c + in + i) + showInst=true + ;; + m) + max=$OPTARG + # and now the obscure +ve integer checking bit + # ...note the creative use of unary - to prevent leading signs + if [ "X-$max" != "X`expr 0 + -$max 2>/dev/null`" ] + then + _pmview_error "$prog: -m must have a positive integral argument" + #NOTREACHED + fi + ;; + v) + if [ $OPTARG = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + elif [ $OPTARG != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + V) + verbose=true + ;; + '?') + _usage + exit 1 + ;; + esac + done +fi +[ -z "$titleArg" ] && titleArg="SGI PCP: Web PING Performance for Host $host" + + +# check that webping metrics are available from metrics source +# +if pminfo $msource $namespace webping 2>&1 \ + | grep 'webping: Unknown metric name' >/dev/null +then + _pmview_error "$prog: webping metrics not defined in the name space" + #NOTREACHED +fi +# +# do a second check to make sure that we have webping.perurl metrics +# +if pminfo -f $msource $namespace webping.perurl.kbytes 2>&1 \ + | grep 'inst .* value' >/dev/null +then + : +else + if [ "X$arch" != "X" ] + then + _pmview_error "$prog: webping.perurl metrics not available from $arch" + #NOTREACHED + else + _pmview_error "$prog: webping.perurl metrics not available from $host" + #NOTREACHED + fi +fi + + +# if metrics source is an archive, and find out name of host +# get the instance domain and sort it +# +if [ "X$arch" != X ] +then + _sort_arch_indom $tmp.int + host=`pmdumplog -l $arch | $PCP_AWK_PROG '/^Performance/ {print $5}' | sed -e 's/,//g'` + [ "X$host" = X ] && host="unknown host" + host="$host (Archive $arch)" +else + pmval -h $host -s 1 webping.perurl.kbytes >$tmp.int + _sort_host_indom $tmp.int +fi + + +urlcount=`wc -l <$tmp.int | sed -e 's/ *//g'` +bytesmax=`tail -1 $tmp.int | $PCP_AWK_PROG '{print $2}'` + +if [ $urlcount -eq 0 ] +then + if [ "X$arch" != "X" ] + then + _pmview_error "$prog: there were no URLs monitored in archive \"$arch\"" + #NOTREACHED + else + _pmview_error "$prog: there are no URLs monitored on host \"$host\"" + #NOTREACHED + fi +fi + + +url_list=`$PCP_AWK_PROG <$tmp.int ' +NR>1 { printf "," } + { printf $1 } +END { print "" }'` + +url_list_sp=`$PCP_AWK_PROG <$tmp.int ' +NR>1 { printf " " } +/^GET_.*/ { printf "\"GET %s\"",substr($1,5) } +/^HEAD_.*/ { printf "\"HEAD %s\"",substr($1,6) } +/^POST_.*/ { printf "\"POST %s\"",substr($1,6) } +END { print "" }'` + + +# +# strings for base plane labels +# +basestr_size="URL size in kbytes\nNormalized to $bytesmax kbytes" +basestr_urltime="Time to fetch the URL\nNormalized to $max seconds" +basestr_tottime="Time to fetch all URLs\nNormalized to $max seconds" +basestr_err="Webing errors by error type\nNormalized to 1" + + +#--- config file has already been created ; continue writing to it --- +# + +cat << end-of-file >> $tmp.pmview +# +# $prog +# +# Largest URL = $bytesmax Kbytes +# Maximum fetch time = $max seconds +# +# URLs = $url_list_sp +# +end-of-file + + +# +# the real config starts here +# pmview Version 2.1 +# +cat <<end-of-file >> $tmp.pmview + +_scale 1.2 + +_colorList url_colors ( + rgbi:1.0/0.5/0.0 + rgbi:0.9/0.9/0.0 + rgbi:0.0/0.9/0.9 + rgbi:0.9/0.0/0.9 +) + +_grid _hide ( + + _bar 0 0 east _col ( + _metrics ( + webping.perurl.kbytes[$url_list] $bytesmax + ) + _metriclabels ( Size ) + _baseLabel "$basestr_size" +end-of-file + +if $showInst +then + cat <<end-of-file >>$tmp.pmview + _instlabels _towards ( $url_list_sp ) +end-of-file +fi + +cat <<end-of-file >>$tmp.pmview + _colorList ( rgbi:0.0/0.9/0.0 ) + ) + + _bar 0 2 ne _col _groupbyinst ( + _metrics ( + webping.perurl.time.total[$url_list] $max + webping.perurl.time.body[$url_list] $max + webping.perurl.time.head[$url_list] $max + webping.perurl.time.connect[$url_list] $max + ) + _metriclabels ( Total Body Head Connect ) + _baseLabel "$basestr_urltime" + _colorList url_colors + ) + + _bar 0 4 east _row _groupbyrow ( + _metrics ( + webping.errors.sockerr 1 + webping.errors.httperr 1 + webping.errors.htmlerr 1 + webping.errors.othererr 1 + ) + _instlabels ( Errors ) + _baseLabel "$basestr_err" + _colorList ( red1 red1 red1 red1 ) + ) + +end-of-file + +# +# Only show row totals if there is more than one URL being monitored +# +if [ $urlcount -gt 1 ] +then + totoff=`expr $urlcount + 1` + cat <<end-of-file >> $tmp.pmview + _bar 2 2 north _col _groupbycol ( + _metrics ( + webping.time.total $max + webping.time.body $max + webping.time.head $max + webping.time.connect $max + ) + _colorList url_colors + _baseLabel "$basestr_tottime" + ) +end-of-file + +fi + +cat <<end-of-file >> $tmp.pmview +) +end-of-file + +$verbose && cat $tmp.pmview + +#eval pmview <$tmp.pmview $args -title "'$titleArg'" -xrm "'*iconName: $prog'" -geometry 560x515 +eval pmview <$tmp.pmview $args -title "'$titleArg'" -xrm "'*iconName: $prog'" + +exit diff --git a/src/pmview/front-ends/webpingvis.rgb b/src/pmview/front-ends/webpingvis.rgb Binary files differnew file mode 100644 index 0000000..a444bec --- /dev/null +++ b/src/pmview/front-ends/webpingvis.rgb diff --git a/src/pmview/front-ends/webvis b/src/pmview/front-ends/webvis new file mode 100755 index 0000000..7969aec --- /dev/null +++ b/src/pmview/front-ends/webvis @@ -0,0 +1,707 @@ +#!/bin/sh +# Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +tmp=/tmp/$$ +trap "rm -f $tmp.*; exit" 0 1 2 3 15 +rm -f $tmp.* + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmview-args + +# --- scaling parameters --- +# + +# maximum packets per second +max=750 + +# maximum req rate (default: 5% of packet rate) +maxreq=`expr $max / 20` + +# maximum disk io rate (I/O operations per second) +maxio=100 + +# maximum average disk business (percent) +maxbusy=30 + +# milliseconds per CPU +maxcpu=1000 + +# maximum TCP output queue length +maxq=5 + +# --- define usage message --- +# +_usage() +{ + echo >$tmp.msg 'Usage: '$prog' [options] [interface ...] + +options: + -b maxbusy maximum average disk active (percent) [default 30] + -i maxio maximum total I/Os per second [default 100] + -m max maximum expected packets sent or received per sec [default 750] + -r maxreq maximum expected Web requests per second [default 35] + -V verbose/diagnostic output + +pmview(1) options:' + + _pmview_usage >> $tmp.msg + echo >> $tmp.msg + echo >> $tmp.msg 'Default title is: Web Server Activity for Host' + echo >> $tmp.msg ' + By default all network interfaces are shown, with a maximum packet rate + of '$max' packets per second. The maximum request rate is 5% (of the + maximum packet rate) and the maximum error rate is 20% of the maximum + request rate. + + If given, the [interface ...] regular expressions restrict + the network statistics displayed to matching network interface names only.' + + _pmview_info -f $tmp.msg +} + +# --- build WM_COMMAND X(1) property for restart after login/logout --- +# +echo -n "pmview Version 2.1 \"$prog\"" > $tmp.pmview +for arg +do + echo -n " \"$arg\"" >>$tmp.pmview +done +echo >> $tmp.pmview + +# --- parse command line arguments --- +# +verbose=false +argInterfaces="" +interfaces="" + +_pmview_args "$@" + +if [ -n "$otherArgs" ] +then + while getopts "?b:i:m:r:v:V" c $otherArgs + do + case $c + in + b) + # and now the obscure +ve integer checking bit + # ...note the creative use of unary - to prevent leading signs + maxbusy=$OPTARG + if [ "X-$maxbusy" != "X`expr 0 + -$maxbusy 2>/dev/null`" ] + then + _pmview_error "$prog: -b must have a positive integral argument" + #NOTREACHED + fi + ;; + + i) + maxio=$OPTARG + if [ "X-$maxio" != "X`expr 0 + -$maxio 2>/dev/null`" ] + then + _pmview_error "$prog: -i must have a positive integral argument" + #NOTREACHED + fi + ;; + + m) + max=$OPTARG + if [ "X-$max" != "X`expr 0 + -$max 2>/dev/null`" ] + then + _pmview_error "$prog: -m must have a positive integral argument" + #NOTREACHED + fi + maxreq=`expr $max / 20` + ;; + + r) + maxreq=$OPTARG + if [ "X-$maxreq" != "X`expr 0 + -$maxreq 2>/dev/null`" ] + then + _pmview_error "$prog: -r must have a positive integral argument" + #NOTREACHED + fi + ;; + + v) + if [ $OPTARG = "1" ] + then + _pmview_warning "$prog: pmview version 1 no longer supported, using version 2" + elif [ $OPTARG != "2" ] + then + _pmview_error "$prog: only version 2 supported for -v" + # NOTREACHED + fi + ;; + + V) + verbose=true + ;; + + '?') + _usage + exit 1 + ;; + esac + done + + set - $otherArgs + shift `expr $OPTIND - 1` + [ $# -gt 0 ] && argInterfaces="$*" +fi + +# maximum average active for disks, percent -> msec/sec +maxactive=`expr $maxbusy \* 1000 / 100` + + +# maximum req error rate (default: 20% of packet rate) +maxerr=`expr $maxreq / 5` +[ "$maxerr" -eq 0 ] && maxerr=1 + + +# --- check that web metrics are available from metrics source --- +# +if _pmview_fetch web.allservers.errors +then + : +else + _pmview_error "$prog: weblog metrics not defined in the name space" + #NOTREACHED +fi + + +# --- if metrics source is an archive, and find out name of host --- +# +if [ "X$arch" != X ] +then + host=`pmdumplog -l $arch | $PCP_AWK_PROG '/^Performance/ {print $5}' | sed -e 's/,//g'` + [ "X$host" = X ] && host="unknown host" + host="$host (Archive $arch)" +fi + + +# --- get network interfaces --- +# +pminfo -f $msource network.interface.total.bytes >$tmp.int +sed -e 's/\]//' -e 's/"//g' $tmp.int \ + | $PCP_AWK_PROG '$1 == "inst" {print $4}' > $tmp.list +if [ ! -s $tmp.list ] +then + if [ "X$arch" != X ] + then + _pmview_error "$prog: failed to get network interface list from archive \"$arch\"" + #NOTREACHED + else + _pmview_error "$prog: failed to get network interface list from host \"$host\"" + #NOTREACHED + fi +fi + + +# --- pmview config file has already been created; keep writing to it --- +# + +cat << end-of-file >> $tmp.pmview +# +# $prog +# +end-of-file + +if $verbose +then + echo "# Available Network Interfaces: " `cat $tmp.list` >> $tmp.pmview +fi + +if [ -z "$argInterfaces" ] +then + cp $tmp.list $tmp.chosen +else + touch $tmp.chosen + for i in $argInterfaces + do + egrep $i $tmp.list >> $tmp.chosen + done +fi + +interfaces_sp=`cat $tmp.chosen | sort | uniq` +loflag=false +for iface in $interfaces_sp +do + if [ "X$iface" = "Xlo0" ] + then + loflag=true + else + interfaces="$interfaces $iface" + fi +done +$loflag && interfaces="$interfaces lo0" +ninterfaces=`echo $interfaces | wc -w` + +if $verbose +then + echo "# Network interfaces Matching \"$argInterfaces\": $interfaces" >> $tmp.pmview +fi + +if [ $ninterfaces = 0 ] +then + echo "$prog: no matching network interfaces" + echo "Available interfaces on host \"$host\" are: " `cat $tmp.list` + echo "" + _usage + exit 1 +fi + +net_grid1=`expr $ninterfaces + 2` + +# --- how many CPUs on this system? --- +# +numcpu=`pminfo $msource -f $namespace hinv.ncpu 2>&1 | $PCP_AWK_PROG ' +BEGIN { num = 0 } +$1 == "value" { if (NF == 2) num = $2 } +END { print num }'` + +if [ $numcpu -lt 1 ] +then + _pmview_fetch_indom kernel.percpu.cpu.user && numcpu=$number + if [ $numcpu -lt 1 ] + then + _pmview_error "$prog: Unable to determine the number of CPUs on host $host" + #NOTREACHED + fi +fi + +maxcpu=`expr $maxcpu \* $numcpu` + +if $verbose +then + if [ $numcpu = 1 ] + then + echo "# 1 CPU detected" >> $tmp.pmview + echo "#" >> $tmp.pmview + else + echo "# $numcpu CPUs detected" >> $tmp.pmview + echo "#" >> $tmp.pmview + fi +fi + +# --- how much memory on this system? --- +# +realmem=0 +if pminfo -v $msource $namespace hinv.physmem > /dev/null 2>&1 +then + realmem=`pminfo -f $msource $namespace hinv.physmem | $PCP_AWK_PROG '/value/ { print $2 }'` + if [ -z "$realmem" ] + then + realmem=0 + else + realmem=`expr $realmem \* 1024` + fi +fi +[ $realmem = 0 ] && echo "$prog: Warning: Unable to determine size of real memory for $host" + + +# --- how many disks on this system? --- +# +numdisk=`pminfo $msource -f $namespace hinv.ndisk 2>&1 | $PCP_AWK_PROG ' +BEGIN { num = 0 } +$1 == "value" { if (NF == 2) num = $2 } +END { print num }'` + +if [ $numdisk -lt 0 ] +then + _pmview_fetch_indom disk.dev.read && numdisk=$number + [ $numdisk -lt 0 ] && _pmview_warning "$prog: Unable to determine the number of disks for $host" +fi + +if $verbose +then + if [ $numdisk = 1 ] + then + echo "# 1 disk detected" >> $tmp.pmview + echo "#" >> $tmp.pmview + else + echo "# $numdisk disks detected" >> $tmp.pmview + echo "#" >> $tmp.pmview + fi +fi + + +# --- set the window title --- +# +if [ -z "$titleArg" ] +then + titleArg="SGI PCP : Web Server Activity for Host $host" +fi + + +# --- set base strings for base plane objects --- +# +if [ $numcpu = 1 ] +then + basestr_cpu="CPU Utilization\nSummed over 1 CPU" +else + basestr_cpu="CPU Utilization\nSummed over $numcpu CPUs" +fi + +basestr_mem="Physical Memory Utilization\nNormalized to `pminfo -f $msource mem.physmem | grep value | sed -e 's/ *value //'` Kbytes" + +basestr_disk="Read and Write activity for all Disks\nNormalized to $maxio I/Os per second" +basestr_diskact="Average Disk Utilization\nNormalized to $maxbusy% across $numdisk disks" + +basestr_net="Input and Output on Network Interfaces\nPackets are normalized to $max packets per second" + +basestr_amem="Memory metrics which may indicate a problem\nNormalized to $maxerr events per second" +basestr_atcp="TCP metrics which may indicate a problem\nNormalized to $maxerr events per second" + +basestr_size="HTTP request rate by response size in bytes\nNormalized to $maxreq hits per second" +basestr_type="HTTP request rate by HTTP method\nNormalized to $maxreq hits per second" + + +# ---- the real config starts here --- +# pmview Version 2.1 +# +cat << end-of-file >> $tmp.pmview + +_stackLength 26 +_marginWidth 8 +_marginDepth 8 + +_colorList cpu_colors ( blue2 red2 yellow2 cyan2 ) +_colorList disk_colors ( violet yellow ) +_colorList memory_colors ( + rgbi:1.0/1.0/0.0 + rgbi:0.0/1.0/1.0 + rgbi:1.0/0.0/0.0 + rgbi:1.0/0.0/1.0 + rgbi:0.0/0.0/1.0 + rgbi:0.0/1.0/0.0 +) +_colorList network_colors ( + rgbi:0.8/0.0/0.0 + rgbi:1.0/0.5/0.0 + rgbi:0.0/0.8/0.0 +) +_colorList type_colors ( + rgbi:0.8/0.0/0.0 + rgbi:1.0/1.0/0.6 + rgbi:1.0/0.0/1.0 + rgbi:0.0/1.0/1.0 + rgbi:1.0/1.0/0.0 +) + +_colorList size_colors ( + rgbi:1.0/0.35/0.0 + rgbi:0.6/0.0/0.9 + rgbi:0.0/1.0/0.0 + rgbi:1.0/0.5/0.0 + rgbi:0.65/0.3/1.0 + rgbi:0.3/1.0/0.3 + rgbi:1.0/0.65/0.3 + rgbi:0.8/0.6/1.0 + rgbi:0.6/1.0/0.6 + rgbi:1.0/0.8/0.6 +) + +_grid _hide ( + +# +# Alarms +# + _grid 1 0 6 2 south _hide ( + _bar 0 1 6 1 west _row _groupbyrow ( + _metrics ( +end-of-file + +for m in drops conndrops timeoutdrop rcvbadsum rexmttime sndrexmitpack attemptfails inerrs retranssegs +do + if _pmview_fetch network.tcp.$m + then + echo " network.tcp.$m $maxerr" >> $tmp.pmview + fi +done + +cat << end-of-file >> $tmp.pmview + ) + _colorList ( + red1 red1 red1 red1 red1 red1 + ) + _baseLabel "$basestr_atcp" + ) + _bar 0 0 3 1 west _row _groupbyrow ( + _metrics ( +end-of-file + +for m in swap.pagesout network.mbuf.failed network.mbuf.waited +do + if _pmview_fetch $m + then + echo " $m $maxerr" >> $tmp.pmview + fi +done + +cat << end-of-file >> $tmp.pmview + ) + _colorList ( + yellow rgbi:1.0/0.5/0.0 rgbi:1.0/0.5/0.0 + ) + _baseLabel "$basestr_amem" + ) + _label 3 0 west _right _medium "Alarms" + ) + +# +# Size +# + _bar 0 1 1 10 south _groupbycol ( + _metrics ( + web.allservers.requests.size.unknown $maxreq + web.allservers.requests.size.gt3m $maxreq + web.allservers.requests.size.le3m $maxreq + web.allservers.requests.size.le1m $maxreq + web.allservers.requests.size.le300k $maxreq + web.allservers.requests.size.le100k $maxreq + web.allservers.requests.size.le30k $maxreq + web.allservers.requests.size.le10k $maxreq + web.allservers.requests.size.le3k $maxreq + web.allservers.requests.size.zero $maxreq + ) + _metriclabels _towards ( + "?" ">3M" "3M" "1M" "300k" + "100k" "30k" "10k" "3k" "0k" + ) + _colorList size_colors + _baseLabel "$basestr_size" + ) + + _label 0 11 northeast _right _medium "Size" + +# +# Type +# + _bar 1 6 1 5 south _groupbycol ( + _metrics ( + web.allservers.errors $maxerr + web.allservers.requests.other $maxreq + web.allservers.requests.post $maxreq + web.allservers.requests.head $maxreq + web.allservers.requests.get $maxreq + ) + _colorList type_colors + _baseLabel "$basestr_type" + ) + + _label 1 11 north _right _medium "Type" + +# +# System level stuff +# + _grid 1 3 5 3 southwest ( + _stack 0 0 ( + _metrics ( + kernel.all.cpu.user $maxcpu + kernel.all.cpu.sys $maxcpu + kernel.all.cpu.intr $maxcpu + kernel.all.cpu.wait.total $maxcpu + ) + _colorList cpu_colors + _baseLabel "$basestr_cpu" + ) + _label 0 1 north _right _medium "CPU" + +# 2 levels of base plane, halve the margin size and increase the height +# for the inner one +# +_marginWidth 4 +_marginDepth 4 +_baseHeight 4 + + _grid 1 0 2 1 _show ( + _baseColor rgbi:0.30/0.30/0.30 + _stack 0 0 south _cylinder ( + _metrics ( + disk.all.write $maxio + disk.all.read $maxio + ) + _colorList disk_colors + _baseLabel "$basestr_disk" + ) +end-of-file + + # disk.all.avg_disk.active metric is not available from pcp 1.x + # + if _pmview_fetch disk.all.avg_disk.active + then + cat << end-of-file >> $tmp.pmview + _bar 1 0 _cylinder ( + _metrics ( + disk.all.avg_disk.active $maxactive + ) + _colorList ( green2 ) + _baseLabel "$basestr_diskact" + ) +end-of-file + fi + + cat << end-of-file >> $tmp.pmview + _baseColor rgbi:0.15/0.15/0.15 + ) + _label 1 1 2 1 north _medium "Disk" + +end-of-file + + if [ "X$realmem" != X0 -a "X$realmem" != X ] + then + xcoord=0 + + cat << end-of-file >> $tmp.pmview + _grid 3 0 2 1 _show ( + _baseColor rgbi:0.30/0.30/0.30 +end-of-file + + if _pmview_fetch mem.freemem + then + cat << end-of-file >> $tmp.pmview + _stack $xcoord 0 ( + _metrics ( + mem.freemem $realmem + ) + _colorList ( rgbi:0.0/0.8/0.0 ) + _baseLabel "Free memory" + ) +end-of-file + xcoord=`expr $xcoord + 1` + fi + + if _pmview_fetch mem.util.kernel + then + cat << end-of-file >> $tmp.pmview + + _stack $xcoord 0 ( + _metrics ( +end-of-file + # Use all the metrics we have + for m in kernel fs_ctl fs_dirty fs_clean user ; do + if _pmview_fetch mem.util.$m + then + echo " mem.util.$m $realmem" >> $tmp.pmview + fi + done + + cat << end-of-file >> $tmp.pmview + ) + _colorList memory_colors + _baseLabel "$basestr_mem" + ) +end-of-file + xcoord=`expr $xcoord + 1` + fi + + cat << end-of-file >> $tmp.pmview + _baseColor rgbi:0.15/0.15/0.15 + ) + _label 3 1 2 1 north _medium "Mem" +end-of-file + fi + + cat << end-of-file >> $tmp.pmview + +# restore defaults +# + _marginWidth 8 + _marginDepth 8 + _baseHeight 2 + ) + +# +# Network Stuff +# + _grid 2 6 $net_grid1 5 south _hide ( + _grid 0 0 1 2 west _hide ( + _label 0 0 east _right _medium "In" + _label 0 1 east _right _medium "Out" + ) +# 2 levels of base plane, halve the margin size and increase the height +# for the inner one +# +_marginWidth 4 +_marginDepth 4 +_baseHeight 4 + + _grid 1 0 $ninterfaces 2 _show ( + _baseColor rgbi:0.30/0.30/0.30 + +end-of-file + + xcoord=0 + yin=0 + yout=1 + for iface in $interfaces + do + cat << end-of-file >> $tmp.pmview + + _stack $xcoord $yin ( + _metrics ( + network.interface.in.errors[$iface] $maxerr + network.interface.in.drops[$iface] $maxerr + network.interface.in.packets[$iface] $max + ) + _colorList network_colors + _baseLabel "$basestr_net" + ) + _stack $xcoord $yout ( + _metrics ( + network.interface.out.errors[$iface] $maxerr + network.interface.out.drops[$iface] $maxerr + network.interface.out.packets[$iface] $max + ) + _colorList network_colors + _baseLabel "$basestr_net" + ) +end-of-file + xcoord=`expr $xcoord + 1` + done + + cat << end-of-file >> $tmp.pmview + _baseLabel "$basestr_net" +_baseColor rgbi:0.15/0.15/0.15 + ) +# restore defaults +# +_marginWidth 8 +_marginDepth 8 +_baseHeight 2 + _grid 1 2 $ninterfaces 2 _hide ( +end-of-file + + xcoord=0 + ylabel=0 + for iface in $interfaces + do + cat << end-of-file >> $tmp.pmview + _label $xcoord $ylabel north _down _medium "$iface" +end-of-file + xcoord=`expr $xcoord + 1` + done + + cat << end-of-file >> $tmp.pmview + ) + ) + _label 3 11 northwest _right _medium "Network" +) +end-of-file + +$verbose && cat $tmp.pmview + +eval $PMVIEW <$tmp.pmview $args -title "'$titleArg'" -xrm "'*iconName: $prog'" + +exit + diff --git a/src/pmview/front-ends/webvis.rgb b/src/pmview/front-ends/webvis.rgb Binary files differnew file mode 100644 index 0000000..5bd1482 --- /dev/null +++ b/src/pmview/front-ends/webvis.rgb diff --git a/src/pmview/gram.y b/src/pmview/gram.y new file mode 100644 index 0000000..5859f94 --- /dev/null +++ b/src/pmview/gram.y @@ -0,0 +1,1391 @@ +/* + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +%{ + +#include <QtCore/QStack> + +#include "main.h" +#include "gridobj.h" +#include "stackobj.h" +#include "barobj.h" +#include "labelobj.h" +#include "pipeobj.h" +#include "scenefileobj.h" +#include "link.h" +#include "xing.h" + +extern void yywarn(const char *s); +extern void yyerror(const char *s); + +#ifdef __cplusplus +extern "C" { +#endif +extern int yylex(void); +#ifdef __cplusplus +} +#endif + +static DefaultObj dobj; +static QStack<ViewObj *> objstack; + +ViewObj * rootObj = 0; + +bool theColorScaleFlag; +int theCol; +int theRow; +int theNumCols; +int theNumRows; +int theHistory = 0; +ViewObj::Alignment theAlignment; +int theColorListCount = 0; + +%} + +%union { + char *y_str; + int y_int; + double y_real; + bool y_bool; + ViewObj::Alignment y_align; + ViewObj::Shape y_shape; + Text::Direction y_textDir; + Text::FontSize y_fontSize; + BarMod::Direction y_instDir; + BarObj::LabelDir y_labelDir; + BarMod::Modulation y_barMod; + StackMod::Height y_stackMod; + BarMod::Grouping y_barGroup; + ViewObj * y_object; +} + +%token <y_str> + NAME STRING PMVIEW METRIC + +%token <y_int> + INT + +%token <y_real> + REAL + +%token <y_bool> + BOOL + +%token <y_align> + ALIGN + +%token <y_textDir> + DIRVAL + +%token <y_fontSize> + SIZ + +%token <y_instDir> + INST_DIR + +%token <y_barMod> + BAR_TYPE + +%token <y_shape> + SHAPE + +%token <y_stackMod> + STACK_TYPE + +%token <y_labelDir> + LABEL_DIR + +%token <y_barGroup> + GROUP + +%token SCALE BAR_HEIGHT BAR_LENGTH MARGIN_WIDTH MARGIN_DEPTH BASE_HEIGHT + BASE_COLOR BASE_LABEL GRID_WIDTH GRID_DEPTH GAP_LABEL GRID_SPACE + GAP_WIDTH GAP_DEPTH COLORLIST OPENB CLOSEB COLORSCALE GRID SHOW HIDE + LABEL LABEL_MARGIN LABEL_COLOR DIRECTION SIZE TEXT BAR METRICLIST + METRICLABEL INSTLABEL STACK STACK_LABEL UTIL AWAY TOWARDS + BOX SOCKET PIPE PIPE_LENGTH PIPETAG GR_LINK GR_XING HISTORY SCENE_FILE + +%type <y_real> + real + +%type <y_bool> + show_or_hide + hide_or_show + +%type <y_textDir> + direction_cmd + +%type <y_fontSize> + size_cmd + +%type <y_instDir> + col_or_row + +%type <y_barMod> + bar_type + +%type <y_shape> + shape_type + +%type <y_stackMod> + stack_type + +%type <y_labelDir> + away_or_towards + +%type <y_barGroup> + bar_group + +%type <y_object> + scene object grid bar stack label pipe link xing scenefile + +%type <y_str> + symname nameval colordecl linktag metricname +%% + +config : header scene; + +header : pmview arglist + | pmview + ; + +pmview : PMVIEW + { + if (strcmp($1, "1.2") < 0) { + pmprintf("Version %s is no longer supported\n", + $1); + pmflush(); + exit(1); + } + } + ; + +arglist : arg + | arglist arg + ; + +arg : STRING + { + extern char **frontend_argv; + extern int frontend_argc; + + frontend_argv = (char **)realloc(frontend_argv, + (frontend_argc+1)*sizeof(char *)); + if (frontend_argv == NULL) { + frontend_argc = 0; + } else { + frontend_argv[frontend_argc] =$1; + if (frontend_argv[frontend_argc] == NULL) { + frontend_argc = 0; + } else { + frontend_argc++; + } + } + } + ; + +scene : object { rootObj = $1; } + | sceneattrlist object { rootObj = $2; } + ; + +object : grid + | label + | bar + | pipe + | link + | xing + | stack + | scenefile + ; + +sceneattrlist : sceneattr + | sceneattrlist sceneattr + ; + +sceneattr : scale + | named_color_list + | named_color_scale + | most_opts + ; + +most_opts : mod_opts + | base_opts + | grid_opts + | bar_opts + | label_opts + | pipe_opts + ; + +scale : SCALE REAL + { + if ($2 <= 0.0) + yyerror("_scale requires a positive real number"); + else + theGlobalScale = $2; + } + ; + +pipe_opts : PIPE_LENGTH INT + { + if ($2 <= 0) + yyerror("_pipeLength requires a positive integer"); + else if ( objstack.empty () ) + dobj.pipeLength() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->pipeLength() = $2; + } + else + yyerror ("cannot change pipe length - not a grid"); + } + ; + +mod_opts : BAR_HEIGHT INT + { + if ($2 <= 0) + yyerror("_barHeight requires a positive integer"); + else if ( objstack.empty () ) + dobj.barHeight() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->barHeight() = $2; + } + else + yyerror ("cannot change bar height - not a grid"); + } + | BAR_LENGTH INT + { + if ($2 <= 0) + yyerror("_barLength requires a positive integer"); + else if ( objstack.empty () ) + dobj.barLength() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->barLength() = $2; + } + else + yyerror ("cannot change bar length - not a grid"); + } + ; + +base_opts : MARGIN_WIDTH INT + { + if ($2 <= 0) + yyerror("_marginWidth requires a positive integer"); + else if ( objstack.empty () ) + dobj.baseBorderX() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->baseBorderX() = $2; + } + else + yyerror("cannot change width margin - not a grid"); + } + | MARGIN_DEPTH INT + { + if ($2 <= 0) + yyerror("_marginDepth requires a positive integer"); + else if ( objstack.empty () ) + dobj.baseBorderZ() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->baseBorderZ() = $2; + } + else + yyerror ("cannot change depth margin - not a grid"); + } + | BASE_HEIGHT INT + { + if ($2 <= 0) + yyerror("_baseHeight requires a positive integer"); + else if ( objstack.empty () ) + dobj.baseHeight() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->baseHeight() = $2; + } + else + yyerror ("cannot change base height - not a grid"); + } + | BASE_COLOR symname + { + float r, g, b; + + if (ColorList::findColor($2, r, g, b) == true ) { + if ( objstack.empty () ) + dobj.baseColor (r,g,b); + else if(objstack.top()->objbits()&ViewObj::GRIDOBJ){ + GridObj * go = + static_cast<GridObj*>(objstack.top()); + + go->defs()->baseColor(r, g, b); + } + else + yyerror ("Cannot change base color - not a grid"); + + } else { + sprintf(theBuffer, + "unable to map _baseColor color \"%s\"", + $2); + yyerror(theBuffer); + } + } + | BASE_COLOR REAL REAL REAL + { + if ( $2 < 0.0 || $2 > 1.0 || + $3 < 0.0 || $3 > 1.0 || + $4 < 0.0 || $4 > 1.0) { + sprintf(theBuffer, + "_baseColor colors %f,%f,%f must be " + "between 0.0 and 1.0", + $2, $3, $4); + yyerror(theBuffer); + } else if ( objstack.empty () ) { + dobj.baseColor($2, $3, $4); + } else if(objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->baseColor($2, $3, $4); + } else { + yyerror("Cannot change base color - not a grid"); + } + } + ; + +grid_opts : GRID_WIDTH INT + { + if ($2 <= 0) + yyerror("_gridWidth requires a positive integer"); + else if ( objstack.empty() ) + dobj.gridMinWidth() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->gridMinWidth() = $2; + } + else + yyerror("Cannot change min width - not a grid"); + } + | GRID_DEPTH INT + { + if ($2 <= 0) + yyerror("_gridDepth requires a positive integer"); + else if ( objstack.empty() ) + dobj.gridMinDepth() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->gridMinDepth() = $2; + } + else + yyerror("Cannot change min depth - not a grid"); + } + | GRID_SPACE INT + { + if ($2 <= 0) { + yyerror("_gridSpace requires a positive integer"); + } else if ( objstack.empty() ) { + dobj.gridMinWidth() = $2; + dobj.gridMinDepth() = $2; + } else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->gridMinWidth() = $2; + go->defs()->gridMinDepth() = $2; + } else { + yyerror("Cannot change grid size - not a grid"); + } + } + ; + +bar_opts : GAP_WIDTH INT + { + if ($2 <= 0) + yyerror("_gapWidth requires a positive integer"); + else if ( objstack.empty() ) + dobj.barSpaceX() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->barSpaceX() = $2; + } + else + yyerror ("Cannot change bar width - not a grid"); + } + | GAP_DEPTH INT + { + if ($2 <= 0) + yyerror("_gapDepth requires a positive integer"); + else if ( objstack.empty() ) + dobj.barSpaceZ() = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->barSpaceZ() = $2; + } + else + yyerror ("Cannot change bar depth - not a grid"); + } + | GAP_LABEL INT + { + if ($2 <= 0) + yyerror("_gapLabel requires a positive integer"); + else if ( objstack.empty() ) + dobj.barSpaceLabel () = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->barSpaceLabel() = $2; + } + else + yyerror ("Cannot change label space - not a grid"); + } + ; + +label_opts : LABEL_MARGIN INT + { + if ($2 <= 0) + yyerror("_labelMargin requires a positive integer"); + else if ( objstack.empty() ) + dobj.labelMargin () = $2; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj*>(objstack.top()); + go->defs()->labelMargin() = $2; + } + else + yyerror ("Cannot change label margin - not a grid"); + } + | LABEL_COLOR symname + { + float r, g, b; + + if (ColorList::findColor($2, r, g, b) == true) { + if ( objstack.empty() ) + dobj.labelColor(r, g, b); + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * go = + static_cast<GridObj*>(objstack.top()); + go->defs()->labelColor(r, g, b); + } + else + yyerror ("Cannot change label color - not a grid"); + } else { + sprintf(theBuffer, "unable to map color \"%s\"", $2); + yyerror(theBuffer); + } + } + | LABEL_COLOR REAL REAL REAL + { + if ($2 < 0.0 || $2 > 1.0 || $3 < 0.0 || $3 > 1.0 || + $4 < 0.0 || $4 > 1.0) { + sprintf(theBuffer, "unable to map color %f,%f,%f", + $2, $3, $4); + yyerror(theBuffer); + } else { + if ( objstack.empty () ) + dobj.labelColor ($2, $3, $4); + else if(objstack.top()->objbits()&ViewObj::GRIDOBJ){ + GridObj * go = + static_cast<GridObj*>(objstack.top()); + go->defs()->labelColor($2, $3, $4); + } + else + yyerror ("Cannot change label color - not a grid"); + } + } + ; + +symname : NAME | STRING; + +named_color_list: COLORLIST NAME OPENB + { + if (theColorLists.add($2) == false) { + sprintf(theBuffer, + "Color list \"%s\" is already defined", $2); + yywarn(theBuffer); + } + } colors CLOSEB + ; + +colors : color + | colors color + ; + +color : symname + { + if (theColorLists.addColor($1) == false) { + sprintf(theBuffer, "Unable to map color \"%s\"", $1); + yywarn(theBuffer); + } + } + | REAL REAL REAL + { + if (theColorLists.addColor($1, $2, $3) == false) { + sprintf(theBuffer, + "Unable to map color %f,%f,%f, " + "values may be out of range", + $1, $2, $3); + yywarn(theBuffer); + } + } + ; + +named_color_scale: COLORSCALE NAME symname OPENB + { + if (theColorLists.list($2) == NULL) { + if (theColorLists.add($2, $3) == false) { + sprintf(theBuffer, + "Unable to map color \"%s\", " + "defaulting to blue", + $3); + } + theColorScaleFlag = true; + } else { + sprintf(theBuffer, + "Color scale \"%s\" is already defined", + $2); + yywarn(theBuffer); + theColorScaleFlag = false; + } + } scaled_color_list CLOSEB + | COLORSCALE NAME REAL REAL REAL OPENB + { + if (theColorLists.list($2) == NULL) { + if (theColorLists.add($2, $3, $4, $5) == false) { + sprintf(theBuffer, + "Unable to map color %f,%f,%f, " + "defaulting to blue", + $3, $4, $5); + } + theColorScaleFlag = true; + } else { + sprintf(theBuffer, + "Color scale \"%s\" is already defined", $2); + yywarn(theBuffer); + theColorScaleFlag = false; + } + } scaled_color_list CLOSEB + ; + +scaled_color_list : scaled_color + | scaled_color_list scaled_color + ; + +scaled_color : symname REAL + { + if (theColorLists.addColor($1, $2) == false) { + sprintf(theBuffer, "Unable to map color \"%s\"", $1); + yywarn(theBuffer); + } + } + | REAL REAL REAL REAL + { + if (theColorLists.addColor($1, $2, $3, $4) == false) { + sprintf(theBuffer, + "Unable to map color %f,%f,%f, " + "values may be out of range", + $1, $2, $3); + yywarn(theBuffer); + } + } + ; + +pos : grid_pos { + theNumCols = 1; + theNumRows = 1; + theAlignment = ViewObj::center; + } + | grid_pos grid_size { + theAlignment = ViewObj::center; + } + | grid_pos alignment { + theNumCols = 1; + theNumRows = 1; + } + | grid_pos grid_size alignment + | /* Empty position */ { + theCol = 0; + theRow = 0; + theNumCols = 1; + theNumRows = 1; + theAlignment = ViewObj::center; + } + ; + +grid_pos : INT INT + { + if ($1 < 0) { + sprintf(theBuffer, + "Column index must be positive, was %d", + $1); + yyerror(theBuffer); + theCol = 0; + } else + theCol = $1; + + if ($2 < 0) { + sprintf(theBuffer, + "Row index must be positive, was %d", + $2); + yyerror(theBuffer); + theRow = 0; + } else + theRow = $2; + } + ; + +grid_size : INT INT + { + if ($1 < 0) { + sprintf(theBuffer, + "Number of columns must be positive, was %d", + $1); + yyerror(theBuffer); + theNumCols = 1; + } else + theNumCols = $1; + if ($2 < 0) { + sprintf(theBuffer, + "Number of rows must be positive, was %d", + $2); + yyerror(theBuffer); + theNumRows = 1; + } else + theNumRows = $2; + } + ; + +alignment : ALIGN { theAlignment = $1; }; + +baselabelspec : BASE_LABEL symname + { + if ( objstack.empty () ) { + yyerror ("Syntax error - no object to label"); + } else if(objstack.top()->objbits() & ViewObj::BASEOBJ){ + BaseObj * bo = static_cast<BaseObj *>(objstack.top()); + int i; + QString str = $2; + + for (i = 0; i < str.size(); i++) { + if (str[i] == '\n') + break; + if (str[i] == '\\' && + i + 1 < str.size() && + str[i + 1] == 'n') { + str[i] = '\n'; + str.remove(i+1, 1); + break; + } + } + + if (i == str.length()) + str.append(QChar('\n')); + + bo->label() = str; + } else { + yyerror ("Syntax error - wrong object"); + } + } + ; + +scenefile : scenefile_decl + { + if ( ($$ = objstack.top()) ) { + $$->finishedAdd(); + } + objstack.pop(); + } + ; + +scenefile_decl : SCENE_FILE pos STRING + { + const DefaultObj * dob = 0; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + + if (dob) { + if ( SceneFileObj * so = new SceneFileObj (*dob, + theCol, theRow, + theNumCols, + theNumRows, + theAlignment) ) { + so->setSceneFileName($3); + objstack.push (so); + } + else + yyerror ( + "Cannot create a scene file object - out of memory"); + } + else + yyerror ( + "Syntax error - Scene File inside simple object!"); + } + ; + +pipe : pipedecl OPENB pipespec CLOSEB + { + if ( ($$ = objstack.top()) ) { + $$->finishedAdd(); + } + objstack.pop(); + } + ; + +pipedecl : PIPE pos + { + if ( theNumRows > 1 && theNumCols > 1 ) { + yyerror ("Diagonal pipes are not supported"); + objstack.push (0); // So that we would pop grid up + } else { + const DefaultObj * dob = 0; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + + if ( dob ) + if ( PipeObj * po = new PipeObj (*dob, + theCol, theRow, + theNumCols, + theNumRows, + theAlignment) ) + objstack.push (po); + else + yyerror ( + "Cannot create a pipe - out of memory"); + else + yyerror ( + "Syntax error - Pipe inside simple object"); + } + } + ; + +pipespec : pipeattr + | pipespec pipeattr + ; + +pipeattr : metric_list + | named_color + | color_list + | pipetag + ; + +pipetag : PIPETAG symname + { + if (objstack.empty () || + ((objstack.top()->objbits() & ViewObj::PIPEOBJ) == 0)) { + yyerror ("No pipe to attach tag to"); + } else { + PipeObj * p = static_cast<PipeObj*>(objstack.top()); + p->setTag($2); + } + } + ; + +link : GR_LINK pos linktag + { + const DefaultObj * dob = 0; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + + if ( dob ) + if ( Link * l = new Link (*dob, theCol, theRow, + theNumCols, theNumRows, + theAlignment) ) { + if ( $3 != NULL ) { + l->setTag ($3); + free ($3); + } + + l->finishedAdd (); + $$ = l; + } else { + yyerror ("Cannot create a link - out of memory"); + } + else + yyerror ("Syntax error - link inside simple object"); + } + ; + +linktag : symname { $$ = strdup ($1); } + | /* nothing */ { $$ = NULL; } + ; + +grid_object : object + { + if ( $1 ) { + if ( (! objstack.empty()) && + (objstack.top()->objbits() & ViewObj::GRIDOBJ) ) { + GridObj * go = + static_cast<GridObj *>(objstack.top()); + go->addObj ($1 , $1->col(), $1->row()); + } else { + yyerror ("Syntax error - no gird to add to"); + } + } + } + | most_opts + | baselabelspec + ; + +grid_object_list : grid_object + | grid_object_list grid_object + ; + +grid : griddecl grid_object_list CLOSEB + { + $$ = objstack.top(); + $$->finishedAdd(); + objstack.pop(); + } + ; + +griddecl : GRID pos hide_or_show OPENB + { + const DefaultObj * dob = 0; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + + if ( dob ) + if (GridObj * go = new GridObj($3, *dob, + theCol, theRow, + theNumCols, theNumRows, + theAlignment)) { + objstack.push (go); + } else { + yyerror ("Cannot create new grid - out of memory"); + } + else + yyerror ("Syntax error - grid inside a simple object"); + } + ; + +hide_or_show : BOOL { $$ = $1; } + | { $$ = false; } + ; + +label : labeldecl OPENB label_stuff CLOSEB + { + $$ = objstack.top(); + $$->finishedAdd(); + objstack.pop(); + } + | labeldecl direction_cmd size_cmd STRING + { + if ( ($$ = objstack.top()) ) { + if ( $$->objbits() & ViewObj::LABELOBJ ) { + LabelObj * lo = static_cast<LabelObj*>($$); + lo->dir() = $2; + lo->size() = $3; + lo->str() = $4; + lo->finishedAdd (); + } + } + objstack.pop(); + } + ; + +labeldecl : LABEL pos + { + const DefaultObj * dob = 0; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + else + yyerror ("Syntax error - label inside simple object"); + + + if ( dob ) { + if (LabelObj * lo = new LabelObj(*dob, + theCol, theRow, + theNumCols, + theNumRows, + theAlignment)) + objstack.push (lo); + else { + yyerror ("Cannot create label - out of memory"); + objstack.push (NULL); + } + } + } + ; + + +direction_cmd : DIRVAL { $$ = $1; } + | { $$ = Text::right; } + ; + +size_cmd : SIZ { $$ = $1; } + | { $$ = Text::medium; } + ; + +label_stuff : label_item + | label_stuff label_item + ; + +label_item : DIRECTION DIRVAL + { + if ( objstack.empty () ) + yyerror ("cannot change direction - no label"); + else if (objstack.top()->objbits() & ViewObj::LABELOBJ) { + LabelObj * lo = + static_cast<LabelObj*>(objstack.top()); + lo->dir() = $2; + } + else + yyerror ("Syntax error - not a label"); + } + | SIZE SIZ + { + if ( objstack.empty () ) + yyerror ("cannot set label size"); + else if (objstack.top()->objbits() & ViewObj::LABELOBJ) { + LabelObj * lo = + static_cast<LabelObj*>(objstack.top()); + lo->size() = $2; + } + else + yyerror ("Syntax error - not a label"); + } + | TEXT symname + { + if ( objstack.empty () ) + yyerror ("cannot set label text"); + else if (objstack.top()->objbits() & ViewObj::LABELOBJ) { + LabelObj * lo = + static_cast<LabelObj*>(objstack.top()); + lo->str() = $2; + } + else + yyerror ("Syntax error - not a label"); + } + ; + +bar : bardecl bar_stuff CLOSEB + { + $$ = objstack.top(); + $$->finishedAdd(); + objstack.pop(); + } + ; + +history : HISTORY INT + { + theHistory = $2; + } + | + ; + +bardecl : BAR pos col_or_row show_or_hide bar_type shape_type bar_group history OPENB + { + BarObj * bo = 0; + + if ( objstack.empty () ) + bo = new BarObj($6, $3, $5, $7, $4, dobj, + theCol, theRow, + theNumCols, theNumRows, theAlignment); + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ){ + GridObj * go = + static_cast<GridObj *>(objstack.top()); + bo = new BarObj($6, $3, $5, $7, $4, *go->defs(), + theCol, theRow, + theNumCols, theNumRows, theAlignment); + } + else + yyerror ("Syntax error - bar inside simple object"); + + if ( bo ) { + bo->setHistory(theHistory); + objstack.push (bo); + } + } + ; + +show_or_hide : BOOL { $$ = $1; } + | { $$ = true; } + ; + +col_or_row : INST_DIR { $$ = $1; } + | { $$ = BarMod::instPerCol; } + ; + +bar_type : BAR_TYPE { $$ = $1; } + | { $$ = BarMod::yScale; } + ; +shape_type : SHAPE { $$ = $1 ; } + | { $$ = ViewObj::cube; } + ; + +bar_group : GROUP { $$ = $1; } + | { $$ = BarMod::groupByMetric; } + ; + +bar_stuff : bar_item + | bar_stuff bar_item + ; + +bar_item : labelled_metric_list + | named_color + | color_list + | metric_labels + | inst_labels + | baselabelspec + ; + +labelled_metric_list: METRICLIST OPENB labelled_metrics CLOSEB; + +labelled_metrics: labelled_metric + | labelled_metrics labelled_metric + ; + +metricname : METRIC | NAME ; + +labelled_metric : metric + | metricname real STRING + { + if ( objstack.empty() ) + yyerror ("No object to add metrics to"); + else if (objstack.top()->objbits() & ViewObj::BAROBJ) { + BarObj * bo = + static_cast<BarObj *>(objstack.top()); + bo->addMetric($1, $2, $3); + } else { + yyerror ("Syntax error - not a bar object"); + } + } + ; + +real : REAL { $$ = $1; } + | INT { $$ = (double)$1; } + ; + +colordecl : COLORLIST NAME { $$ = $2; } + | COLORSCALE NAME { $$ = $2; } + ; + +named_color : colordecl + { + if ( objstack.empty() ) + yyerror ("No object to add colors to"); + else if (objstack.top()->objbits() & ViewObj::MODOBJ) { + ModObj * mo = + static_cast<ModObj *>(objstack.top()); + mo->setColorList ($1); + } else { + sprintf (theBuffer, + "Syntax error - %s cannot have colors", + objstack.top()->name()); + yyerror (theBuffer); + } + } + ; + +color_list : COLORLIST OPENB + { + if ( objstack.empty() ) + yyerror ("No object to add colors to"); + else if (objstack.top()->objbits() & ViewObj::MODOBJ) { + ModObj * mo = static_cast<ModObj *>(objstack.top()); + sprintf(theBuffer, "@tmp%d", theColorListCount++); + theColorLists.add(theBuffer); + mo->setColorList (theBuffer); + } else { + sprintf (theBuffer, + "Syntax error - %s cannot have colors", + objstack.top()->name()); + yyerror (theBuffer); + } + } colors CLOSEB + | COLORSCALE symname OPENB + { + if ( objstack.empty() ) + yyerror ("No object to add colors to"); + else if (objstack.top()->objbits() & ViewObj::MODOBJ) { + ModObj * mo = static_cast<ModObj *>(objstack.top()); + sprintf(theBuffer, "@tmp%d", theColorListCount++); + theColorLists.add(theBuffer, $2); + mo->setColorList (theBuffer); + } else { + sprintf (theBuffer, + "Syntax error - %s cannot have colors", + objstack.top()->name()); + yyerror (theBuffer); + } + } scaled_color_list CLOSEB + | COLORSCALE REAL REAL REAL OPENB + { + if ( objstack.empty() ) + yyerror ("No object to add colors to"); + else if (objstack.top()->objbits() & ViewObj::MODOBJ) { + ModObj * mo = static_cast<ModObj *>(objstack.top()); + + sprintf(theBuffer, "@tmp%d", theColorListCount++); + theColorLists.add(theBuffer, $2, $3, $4); + mo->setColorList (theBuffer); + } else { + sprintf (theBuffer, + "Syntax error - %s cannot have colors", + objstack.top()->name()); + yyerror (theBuffer); + } + } scaled_color_list CLOSEB + ; + +away_or_towards : LABEL_DIR { $$ = $1; } + | { $$ = BarObj::towards; } + ; + +metric_labels : metriclabeldecl + | metriclabeldecl OPENB metric_name_list CLOSEB + ; + +metriclabeldecl : METRICLABEL away_or_towards + { + if ( objstack.empty () ) + yyerror ("No object to add metric labels to"); + else if (objstack.top()->objbits() & ViewObj::BAROBJ) { + BarObj * bo = static_cast<BarObj *>(objstack.top()); + bo->metricLabelDir() = $2; + } + else + yyerror ("Syntax error - not a bar object"); + } + ; + +metric_name_list: metric_name + | metric_name_list metric_name + ; + +metric_name : nameval + { + if ( objstack.empty() ) + yyerror ("No object to add metric names to"); + else if (objstack.top()->objbits() & ViewObj::BAROBJ) { + BarObj * bo = static_cast<BarObj *>(objstack.top()); + bo->addMetricLabel($1); + } + else + yyerror ("Syntax error - not a bar object"); + free ($1); + } + ; + +nameval : symname { $$ = strdup ($1); } + | INT { sprintf(theBuffer,"%d",$1); $$ = strdup (theBuffer); } + | REAL { sprintf(theBuffer,"%f",$1); $$ = strdup (theBuffer); } + ; + +inst_labels : INSTLABEL away_or_towards OPENB inst_name_list CLOSEB + { + if ( objstack.empty () ) + yyerror ("No object to add instance labels to"); + else if (objstack.top()->objbits() & ViewObj::BAROBJ) { + BarObj * bo = static_cast<BarObj *>(objstack.top()); + bo->instLabelDir() = $2; + } + else + yyerror ("Syntax error - not a bar object"); + + } + ; + +inst_name_list : inst_name + | inst_name_list inst_name + ; + +inst_name : nameval + { + if ( objstack.empty() ) + yyerror ("No object to add instance labels to"); + else if (objstack.top()->objbits() & ViewObj::BAROBJ) { + BarObj * bo = static_cast<BarObj *>(objstack.top()); + bo->addInstLabel($1); + } + else + yyerror ("Syntax error - not a bar object"); + free ($1); + } + ; + +stack : stackdecl stack_stuff CLOSEB + { + $$ = objstack.top(); + $$->finishedAdd(); + objstack.pop(); + } + ; + +stackdecl : STACK pos show_or_hide stack_type shape_type history OPENB + { + StackObj * so = 0; + + if ( objstack.empty () ) + so = new StackObj($4, $5, $3, dobj, + theCol, theRow, + theNumCols, theNumRows, + theAlignment); + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj *>(objstack.top()); + so = new StackObj($4, $5, $3, *go->defs(), + theCol, theRow, + theNumCols, theNumRows, + theAlignment); + } + else + yyerror ("Syntax error - stack inside simple object"); + + if ( so ) { + so->setHistory(theHistory); + objstack.push (so); + } + } + | UTIL pos OPENB + { + StackObj * so = 0; + + if ( objstack.empty () ) + so = new StackObj(StackMod::unfixed, ViewObj::cube, + false, dobj, + theCol, theRow, + theNumCols, theNumRows, + theAlignment); + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * go = static_cast<GridObj *>(objstack.top()); + + so = new StackObj(StackMod::unfixed, ViewObj::cube, + false, *go->defs(), + theCol, theRow, + theNumCols, theNumRows, + theAlignment); + } + else + yyerror ("Syntax error - stack inside simple object"); + + if ( so ) { + objstack.push (so); + } + } + ; + +stack_type : STACK_TYPE { $$ = $1; } + | { $$ = StackMod::unfixed; } + ; + +stack_stuff : stack_item + | stack_stuff stack_item + ; + +stack_item : metric_list + | named_color + | color_list + | baselabelspec + | STACK_LABEL symname + { + if ( objstack.empty() ) { + yyerror ("Syntax error - no stack to label"); + } else if (objstack.top()->objbits() & ViewObj::STACKOBJ) { + StackObj * so = static_cast<StackObj*>(objstack.top()); + + if (so->height() == StackMod::fixed) { + int i; + + QString str = $2; + for (i = 0; i < str.size(); i++) { + if (str[i] == '\n') + break; + if (str[i] == '\\' && + i + 1 < str.size() && + str[i + 1] == 'n') { + str[i] = '\n'; + str.remove(i+1, 1); + break; + } + } + if (i == str.length()) + str.append(QChar('\n')); + so->setFillText((const char *)str.toAscii()); + } else { + yyerror("_stackLabel may only be applied to " + "filled stacks"); + } + } else { + yyerror ("Syntax error - not a stack"); + } + } + ; + +metric_list : METRICLIST OPENB metrics CLOSEB; + +metrics : metric + | metrics metric + ; + +metric : metricname real + { + if ( objstack.empty () ) + yyerror ("Syntax error - no object"); + else if (objstack.top()->objbits() & ViewObj::MODOBJ) { + ModObj * mo = static_cast<ModObj *>(objstack.top()); + mo->addMetric($1, $2); + } + else + yyerror ("The object has no metrics"); + } + ; + +xing : GR_XING INT INT INT INT ALIGN ALIGN ALIGN ALIGN + { + const DefaultObj * dob = 0; + ViewObj::Alignment c[4] = { $6, $7, $8, $9}; + + if ( objstack.empty() ) + dob = & dobj; + else if (objstack.top()->objbits() & ViewObj::GRIDOBJ) { + GridObj * parent = + static_cast<GridObj *>(objstack.top()); + dob = parent->defs(); + } + else + yyerror ("Syntax error - label inside simple object"); + + if ( Xing * xo = new Xing (*dob, $2, $3, $4, $5, c) ) { + xo->finishedAdd (); + $$ = xo; + } else { + $$ = 0; + } + } + ; +%% diff --git a/src/pmview/gridobj.cpp b/src/pmview/gridobj.cpp new file mode 100644 index 0000000..cd0f762 --- /dev/null +++ b/src/pmview/gridobj.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoCube.h> +#include "gridobj.h" +#include "defaultobj.h" + +#include <iostream> +using namespace std; + +GridObj::~GridObj() +{ +} + +GridObj::GridObj(bool onFlag, + const DefaultObj &defaults, + int x, int y, + int cols, int rows, + GridObj::Alignment align) + : BaseObj(onFlag, defaults, x, y, cols, rows, align), + _minDepth(defaults.gridMinDepth()), + _minWidth(defaults.gridMinWidth()), + _width(_minWidth + baseWidth()), + _depth(_minDepth + baseDepth()), + _list(), + _rowDepth(1, _minDepth), + _colWidth(1, _minWidth), + _finished(false) +{ + _objtype |= GRIDOBJ; + _defs = defaults; +} + +void +GridObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + int i, j; + int totalWidth; + int totalDepth; + float xShift = width() / 2.0; + float zShift = depth() / 2.0; + QVector<int> rowPos(rows()); + QVector<int> colPos(cols()); + + assert(_finished == true); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "\nGridObj(" << width() << "x" << depth() << ")@" + << col() << "," << row() + << "::setTran (" + << xTran << ", " << zTran << ", " + << setWidth << ", " << setDepth << ")" << endl; +#endif + + BaseObj::setBaseSize(width(), depth()); + BaseObj::setTran(xTran + xShift, zTran + zShift, setWidth, setDepth); + + colPos[0] = 0; + for (i = 1; i < cols(); i++) { + colPos[i] = colPos[i-1] + _colWidth[i-1]; + } + rowPos[0] = 0; + for (i = 1; i < rows(); i++) { + rowPos[i] = rowPos[i-1] + _rowDepth[i-1]; + } + + for (i = 0; i < _list.size(); i++) { + GridItem &item = _list[i]; + totalWidth = totalDepth = 0; + for (j = item._col; j < item._col + item._item->cols(); j++) + totalWidth += _colWidth[j]; + for (j = item._row; j < item._row + item._item->rows(); j++) + totalDepth += _rowDepth[j]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "GridObj::setTran: [" << i << "] at " << item._col + << ',' << item._row << ": "; +#endif + + item._item->setTran(colPos[item._col] - xShift + borderX(), + rowPos[item._row] - zShift + borderZ(), + totalWidth, totalDepth); + } +} + +int +addRowCols(QVector<int> & ivec, int nsize, int min) +{ + int extra = 0; + + if (nsize > ivec.size()) { + extra = min * (nsize - ivec.size()); + + ivec.resize(nsize); + } + + return extra; +} + +void +GridObj::addObj(ViewObj *obj, int col, int row) +{ + int i; + GridItem newItem; + + for (i = 0; i < _list.size(); i++) { + const GridItem &item = _list[i]; + if ((row >= item._row && row < (item._row + item._item->rows())) && + (col >= item._col && col < (item._col + item._item->cols()))) { + pmprintf("%s: %s at %d,%d (%dx%d) collides with %s at %d,%d (%dx%d)\nThe later object will be ignored\n", + pmProgname, item._item->name(), item._col, + item._row, item._item->cols(), item._item->rows(), + obj->name(), col, row, obj->cols(), obj->rows()); + return; + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::addObj: Adding item " << _list.length() << ": " + << obj->name() << ", size = " << obj->width() << 'x' + << obj->depth() << endl; +#endif + + newItem._item = obj; + newItem._row = row; + newItem._col = col; + _list.append(newItem); + + // Add extra cols & rows as necessary + _width += addRowCols (_colWidth, col + obj->cols(), _minWidth); + _depth += addRowCols (_rowDepth, row + obj->rows(), _minDepth); + + + // Fasttrack size adjustments for simple objects + if (obj->cols() == 1 && _colWidth[col] < obj->width()) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::addObj: increasing col[" << col << "] from " + << _colWidth[col] << " to " << obj->width() << endl; +#endif + _width += obj->width() - _colWidth[col]; + _colWidth[col] = obj->width(); + } + + if (obj->rows() == 1 && _rowDepth[row] < obj->depth()) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::addObj: increasing row[" << row << "] from " + << _rowDepth[row] << " to " << obj->depth() << endl; +#endif + _depth += obj->depth() - _rowDepth[row]; + _rowDepth[row] = obj->depth(); + } +} + +void +GridObj::finishedAdd() +{ + int i, j; + int size; + int current; + int adjust; + + BaseObj::addBase(_root); + + for (i= 0; i < _list.size(); i++) { + const GridItem &item = _list[i]; + _root->addChild(item._item->root()); + if (item._item->modObj() != NULL) { + BaseObj::add(item._item->modObj()); + } + + if (item._item->cols() > 1) { + size = item._item->width(); + for (j = item._col, current = 0; + j < item._col + item._item->cols(); + j++) { + current += _colWidth[j]; + } + + if (current < size) { + size -= current; + adjust = (int)((size / (float)item._item->cols()) + 0.5); + for (j = item._col; + j < item._col + item._item->cols() && adjust > 0; j++) { + if (adjust > size) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::finishedAdd: increasing col[" + << j << "] from " << _colWidth[j] << " to " + << _colWidth[j] + size << endl; +#endif + _colWidth[j] += size; + _width+= size; + adjust = 0; + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::finishedAdd: increasing col[" + << j << "] from " << _colWidth[j] << " to " + << _colWidth[j] + adjust << endl; +#endif + _colWidth[j] += adjust; + _width += adjust; + size -= adjust; + } + } + } + } + + if (item._item->rows() > 1) { + size = item._item->depth(); + for (j = item._row, current = 0; + j < item._row + item._item->rows(); + j++) { + current += _rowDepth[j]; + } + + if (current < size) { + size -= current; + adjust = (int)((size / (float)item._item->rows()) + 0.5); + for (j = item._row; + j < item._row + item._item->rows() && adjust > 0; j++) { + if (adjust > size) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::finishedAdd: increasing row[" + << j << "] from " << _rowDepth[j] << " to " + << _rowDepth[j] + size << endl; +#endif + _rowDepth[j] += size; + _depth+= size; + adjust = 0; + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::finishedAdd: increasing row[" + << j << "] from " << _rowDepth[j] << " to " + << _rowDepth[j] + adjust << endl; +#endif + _rowDepth[j] += adjust; + _depth += adjust; + size -= adjust; + } + } + } + } + } + + _finished = true; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "GridObj::finishedAdd: " << *this << endl; +#endif +} + +QTextStream& +operator<<(QTextStream& os, GridObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +GridObj::display(QTextStream& os) const +{ + int i; + int sum; + + BaseObj::display(os); + os << ", minRowDepth = " << _minDepth << ", minColWidth = " + << _minWidth << ", rows = " << rows() << ", cols = " << cols() + << ", finishedAdd = " << (_finished == true ? "true" : "false") + << endl; + + os << "Column widths: "; + for (i = 0, sum = 0; i < _colWidth.size(); i++) { + os << _colWidth[i] << ' '; + sum += _colWidth[i]; + } + os << endl; + assert(_width == sum + baseWidth()); + + os << "Row depths: "; + for (i = 0, sum = 0; i < _rowDepth.size(); i++) { + os << _rowDepth[i] << ' '; + sum += _rowDepth[i]; + } + os << endl; + assert(_depth == sum + baseDepth()); + + for (i = 0; i < _list.size(); i++) + os << '[' << i << "] at " << _list[i]._col << ',' << _list[i]._row + << ": " << *(_list[i]._item) << endl; +} diff --git a/src/pmview/gridobj.h b/src/pmview/gridobj.h new file mode 100644 index 0000000..f19dbd4 --- /dev/null +++ b/src/pmview/gridobj.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _GRIDOBJ_H_ +#define _GRIDOBJ_H_ + +#include "baseobj.h" +#include "defaultobj.h" +#include <QtCore/QVector> + +struct GridItem +{ + ViewObj *_item; + int _row; + int _col; +}; + +typedef QList<GridItem> GridList; + +class GridObj : public BaseObj +{ +protected: + + int _minDepth; + int _minWidth; + int _width; + int _depth; + GridList _list; + QVector<int> _rowDepth; + QVector<int> _colWidth; + bool _finished; + DefaultObj _defs; + + static int theDefRowDepth; + static int theDefColWidth; + +public: + + virtual ~GridObj(); + + GridObj(bool onFlag, + const DefaultObj &defaults, + int x, int y, + int cols = 1, int rows = 1, + GridObj::Alignment align = GridObj::center); + + DefaultObj * defs() { return & _defs; } + + int numObj() const + { return _list.size(); } + int minDepth() const + { return _minDepth; } + int minWidth() const + { return _minWidth; } + int rows() const + { return _rowDepth.size(); } + int cols() const + { return _colWidth.size(); } + + // Local changes + int &minDepth() + { return _minDepth; } + int &minWidth() + { return _minWidth; } + + virtual int width() const + { return _width; } + virtual int depth() const + { return _depth; } + + virtual void setTran(float xTran, float zTran, int width, int depth); + + void addObj(ViewObj *obj, int col, int row); + + virtual void finishedAdd(); + + // Output + virtual void display(QTextStream& os) const; + + virtual const char* name() const + { return "Grid"; } + + friend QTextStream& operator<<(QTextStream& os, GridObj const& rhs); + +protected: + + void add(Modulate *mod); + +private: + + GridObj(); + GridObj(GridObj const &); + GridObj const& operator=(GridObj const &); +}; + +#endif /* _GRIDOBJ_H_ */ diff --git a/src/pmview/icon b/src/pmview/icon Binary files differnew file mode 100644 index 0000000..e1650fb --- /dev/null +++ b/src/pmview/icon diff --git a/src/pmview/labelobj.cpp b/src/pmview/labelobj.cpp new file mode 100644 index 0000000..c8ec594 --- /dev/null +++ b/src/pmview/labelobj.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> + +#include "pmview.h" +#include "labelobj.h" +#include "defaultobj.h" + +LabelObj::~LabelObj() +{ + if (_text) + delete _text; +} + +LabelObj::LabelObj(const DefaultObj &defaults, + int x, int y, + int cols, int rows, + ViewObj::Alignment align) +: ViewObj(x, y, cols, rows, align), + _str(), + _text(0), + _dir(Text::right), + _fontSize(Text::medium), + _margin(defaults.labelMargin()) +{ + _objtype |= LABELOBJ; + + _color[0] = defaults.labelColor(0); + _color[1] = defaults.labelColor(1); + _color[2] = defaults.labelColor(2); +} + +LabelObj::LabelObj(Text::Direction dir, + Text::FontSize fontSize, + const DefaultObj &defaults, + int x, int y, + int cols, int rows, + ViewObj::Alignment align) +: ViewObj(x, y, cols, rows, align), + _str(), + _text(0), + _dir(dir), + _fontSize(fontSize), + _margin(defaults.labelMargin()) +{ + _color[0] = defaults.labelColor(0); + _color[1] = defaults.labelColor(1); + _color[2] = defaults.labelColor(2); +} + +void +LabelObj::finishedAdd() +{ + _text = new Text(_str, _dir, _fontSize); + SoBaseColor *base = new SoBaseColor; + base->rgb.setValue(_color[0], _color[1], _color[2]); + _root->addChild(base); + _root->addChild(_text->root()); +} + +void +LabelObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + switch(_text->dir()) { + case Text::right: + ViewObj::setTran(xTran + width() - _margin, + zTran + _margin, + setWidth, setDepth); + break; + case Text::down: + ViewObj::setTran(xTran + _margin, + zTran + depth() - _margin, + setWidth, setDepth); + break; + default: + ViewObj::setTran(xTran + _margin, + zTran + _margin, + setWidth, setDepth); + break; + } +} + +QTextStream& +operator<<(QTextStream& os, LabelObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +LabelObj::display(QTextStream& os) const +{ + ViewObj::display(os); + os << ", label = \"" << _str << "\", margin = " << _margin << ", text = "; + if (_text == NULL) + os << "undefined"; + else + os << *_text; +} + diff --git a/src/pmview/labelobj.h b/src/pmview/labelobj.h new file mode 100644 index 0000000..5a0f31c --- /dev/null +++ b/src/pmview/labelobj.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _LABELOBJ_H_ +#define _LABELOBJ_H_ + +#include "text.h" +#include "viewobj.h" + +class SoNode; +class SoSeparator; +class SoTranslation; + +class LabelObj : public ViewObj +{ +protected: + + QString _str; + Text *_text; + Text::Direction _dir; + Text::FontSize _fontSize; + int _margin; + float _color[3]; + +public: + + virtual ~LabelObj(); + + LabelObj(Text::Direction dir, + Text::FontSize fontSize, + const DefaultObj &defaults, + int x, int y, + int cols = 1, int rows = 1, + Alignment align = center); + + LabelObj(const DefaultObj &defaults, + int x, int y, + int cols = 1, int rows = 1, + Alignment align = center); + + const QString &str() const + { return _str; } + Text::Direction dir() const + { return _dir; } + Text::FontSize size() const + { return _fontSize; } + int margin() const + { return _margin; } + float color(int i) const + { return _color[i]; } + + // Local Changes + QString &str() + { return _str; } + Text::Direction &dir() + { return _dir; } + Text::FontSize &size() + { return _fontSize; } + int &margin() + { return _margin; } + void color(float r, float g, float b) + { _color[0] = r; _color[1] = g; _color[2] = b; } + + virtual int width() const + { return _text->width() + (_margin * 2); } + virtual int depth() const + { return _text->depth() + (_margin * 2); } + + virtual void setTran(float xTran, float zTran, int width, int depth); + + virtual void finishedAdd(); + + // Output + virtual void display(QTextStream& os) const; + + virtual const char* name() const + { return "Label"; } + + friend QTextStream& operator<<(QTextStream& os, LabelObj const& rhs); + +private: + + LabelObj(); + LabelObj(LabelObj const &); + LabelObj const& operator=(LabelObj const &); +}; + +#endif /* _LABELOBJ_H_ */ diff --git a/src/pmview/launch.cpp b/src/pmview/launch.cpp new file mode 100644 index 0000000..3e2d2f6 --- /dev/null +++ b/src/pmview/launch.cpp @@ -0,0 +1,421 @@ +/* + * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "main.h" +#include "launch.h" +#include "colorscale.h" +#include "metriclist.h" + +#include <iostream> +using namespace std; + +#define PM_LAUNCH_VERSION1 1 +#define PM_LAUNCH_VERSION2 2 + +const QString Launch::theVersion1Str = "pmlaunch Version 1.0\n"; +const QString Launch::theVersion2Str = "pmlaunch Version 2.0\n"; + +Launch::~Launch() +{ +} + +Launch::Launch(const QString &version) +: _strings(), + _groupMetric(-1), + _groupCount(0), + _groupHint(), + _metricCount(0) +{ + if (version == "1.0") + _version = PM_LAUNCH_VERSION1; + else + _version = PM_LAUNCH_VERSION2; +} + +Launch::Launch(const Launch &rhs) +: _strings(rhs._strings), + _groupMetric(rhs._groupMetric), + _groupCount(rhs._groupCount), + _groupHint(rhs._groupHint), + _metricCount(rhs._metricCount), + _version(rhs._version) +{ +} + +const Launch & +Launch::operator=(const Launch &rhs) +{ + if (this != &rhs) { + _strings = rhs._strings; + _groupMetric = rhs._groupMetric; + _groupCount = rhs._groupCount; + _groupHint = rhs._groupHint; + _metricCount = rhs._metricCount; + } + return *this; +} + +void +Launch::setDefaultOptions(int interval, + int debug, + const char *timeport, + const char *starttime, + const char *endtime, + const char *offset, + const char *timezone, + const char *defsourcetype, + const char *defsourcename, + bool selected) +{ + addOption("interval", (interval < 0 ? -interval : interval)); + addOption("debug", debug); + + if (timeport != NULL) + addOption("timeport", timeport); + if (starttime != NULL) + addOption("starttime", starttime); + if (endtime != NULL) + addOption("endtime", endtime); + if (offset != NULL) + addOption("offset", offset); + if (timezone != NULL) + addOption("timezone", timezone); + if (defsourcetype != NULL) + addOption("defsourcetype", defsourcetype); + if (defsourcename != NULL) + addOption("defsourcename", defsourcename); + if (pmProgname != NULL) + addOption("progname", pmProgname); + addOption("pid", (int)getpid()); + + if (selected) + addOption("selected", "true"); + else + addOption("selected", "false"); +} + +void +Launch::addOption(const char *name, const char *value) +{ + QString str = "option "; + + if (name == NULL) + return; + + if (value == NULL) { + str.append(name).append("=\n"); + } + else { + str.append(name).append('=').append(value).append('\n'); + str.append('\n'); + } + _strings.append(str); +} + +void +Launch::addOption(const char *name, int value) +{ + QString str = "option "; + + if (name == NULL) + return; + + str.append(name).append('=').setNum(value).append('\n'); + _strings.append(str); +} + +void +Launch::addMetric(const QmcMetric &metric, + const SbColor &color, + int instance, + bool useSocks) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + cerr << "Launch::addMetric(1): Adding "; + metric.dump(cerr, true, instance); + cerr << " (" << _metricCount << ')' << endl; + } +#endif + + QmcSource source = metric.context()->source(); + QByteArray ba; + ba = metric.instName(instance).toLocal8Bit(); + + addMetric(metric.context()->handle(), source.source(), source.host(), metric.name(), + metric.hasInstances() == 0 ? NULL : ba.data(), + metric.desc(), color, metric.scale(), useSocks); +} + +void +Launch::addMetric(const QmcMetric &metric, + const ColorScale &scale, + int instance, + bool useSocks) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + cerr << "Launch::addMetric(2): Adding "; + metric.dump(cerr, true, instance); + cerr << " (" << _metricCount << ')' << endl; + } +#endif + + QmcSource source = metric.context()->source(); + QByteArray ba; + ba = metric.instName(instance).toLocal8Bit(); + + addMetric(metric.context()->handle(), source.source(), source.host(), metric.name(), + metric.hasInstances() == 0 ? NULL : ba.data(), + metric.desc(), scale, useSocks); +} + +void +Launch::addMetric(int context, + const QString &source, + const QString &host, + const QString &metric, + const char *instance, + const QmcDesc &desc, + const SbColor &color, + double scale, + bool useSocks) +{ + QString str(256); + QString col; + + if (_groupMetric == -1) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Launch::addMetric: Called before startGroup." + << " Adding a group." << endl; +#endif + startGroup(); + } + + preColor(context, source, host, metric, useSocks, str); + + str.append(" S "); + MetricList::toString(color, col); + str.append(col).append(',').setNum(scale).append(' '); + + postColor(desc, instance, str); + _strings.append(str); +} + +void +Launch::addMetric(int context, + const QString &source, + const QString &host, + const QString &metric, + const char *instance, + const QmcDesc &desc, + const ColorScale &scale, + bool useSocks) +{ + int i; + QString str(128); + QString col; + + if (_groupMetric == -1) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Launch::addMetric: Called before startGroup." + << " Adding a group." << endl; +#endif + startGroup(); + } + + preColor(context, source, host, metric, useSocks, str); + str.append(" D "); + + MetricList::toString(scale[0].color(), col); + str.append(col).append(',').setNum(scale[0].min()); + for (i = 1; i < scale.numSteps(); i++) { + MetricList::toString(scale[i].color(), col); + str.append(',').append(col).append(','); + str.setNum(scale[i].min()); + } + str.append(' '); + + postColor(desc, instance, str); + _strings.append(str); +} + +void +Launch::preColor(int context, + const QString &source, + const QString &host, + const QString &metric, + bool useSocks, + QString &str) +{ + str.append("metric ").setNum(_metricCount++).append(' '); + str.setNum(_groupCount).append(' ').append(_groupHint); + + switch(context) { + case PM_CONTEXT_LOCAL: + str.append(" l "); + break; + case PM_CONTEXT_HOST: + str.append(" h "); + break; + case PM_CONTEXT_ARCHIVE: + str.append(" a "); + break; + } + + str.append(source).append(' '); + + if (context == PM_CONTEXT_ARCHIVE) + str.append(host); + else if (useSocks) + str.append("true"); + else + str.append("false"); + + str.append(' ').append(metric); +} + +void +Launch::postColor(const QmcDesc &desc, + const char *instance, + QString &str) +{ + const pmDesc d = desc.desc(); + + if (_version == PM_LAUNCH_VERSION2) { + str.setNum(d.type).append(' '); + str.setNum(d.sem).append(' '); + str.setNum(d.units.scaleSpace).append(' '); + str.setNum(d.units.scaleTime).append(' '); + str.setNum(d.units.scaleCount).append(' '); + } + + str.setNum(d.units.dimSpace).append(' '); + str.setNum(d.units.dimTime).append(' '); + str.setNum(d.units.dimCount).append(' '); + str.setNum((int)(d.indom)).append(" ["); + if (instance != NULL) + str.append(instance); + str.append("]\n"); +} + +void +Launch::startGroup(const char *hint) +{ + + if (_groupMetric != -1) + cerr << "Launch::startGroup: Two groups started at once!" << endl; + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Launch::startGroup: Starting group " << _groupCount + << endl; +#endif + + _groupMetric = _metricCount; + _groupHint = hint; + } +} + +void +Launch::endGroup() +{ + if (_groupMetric == -1) + cerr << "Launch::endGroup: No group to end!" << endl; + else if (_groupMetric == _metricCount) { + cerr << "Launch::endGroup: No metrics added to group " + << _groupCount << endl; + _groupMetric = -1; + } else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Launch::endGroup: ending group " << _groupCount + << endl; +#endif + + _groupMetric = -1; + _groupCount++; + } +} + +void +Launch::append(Launch const &rhs) +{ + if (rhs._groupMetric != -1) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Launch::append: Group not finished in appended object." + << " Completing group" << endl; +#endif + + // Cast away const, yuk. + ((Launch *)(&rhs))->endGroup(); + } + _strings.append(rhs._strings); +} + +QTextStream& +operator<<(QTextStream& os, Launch const& rhs) +{ + int i; + for (i = 0; i < rhs._strings.size(); i++) + os << rhs._strings[i]; + return os; +} + +const char * +Launch::launchPath() +{ + char *env; + static char launch_path[MAXPATHLEN]; + + if ((env = getenv("PM_LAUNCH_PATH")) != NULL) + strncpy(launch_path, env, sizeof(launch_path)); + else + snprintf(launch_path, sizeof(launch_path), "%s/config/pmlaunch", pmGetConfig("PCP_VAR_DIR")); + return launch_path; +} + +#include <iostream> + +void +Launch::output(int fd) const +{ + QByteArray ba; + int sts; + const char *str; + + if (_version == PM_LAUNCH_VERSION2) + ba = theVersion2Str.toLocal8Bit(); + else + ba = theVersion1Str.toLocal8Bit(); + + str = ba.data(); + if ((sts = write(fd, str, strlen(str))) != (int)strlen(str)) { + cerr << "Launch::output: version write->" << sts + << " not " << strlen(str) << endl; + } + + for (int i = 0; i < _strings.length(); i++) { + ba = _strings[i].toLocal8Bit(); + str = ba.data(); + if ((sts = write(fd, str, strlen(str))) != (int)strlen(str)) { + cerr << "Launch::output: string write->" << sts + << " not " << strlen(str) + << " for " << str << endl; + } + } +} diff --git a/src/pmview/launch.h b/src/pmview/launch.h new file mode 100644 index 0000000..2f048b1 --- /dev/null +++ b/src/pmview/launch.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _LAUNCH_H_ +#define _LAUNCH_H_ + +#include "main.h" +#include <QtCore/QStringList> + +class SbColor; +class ColorScale; + +class Launch +{ +private: + + static const QString theVersion1Str; + static const QString theVersion2Str; + + QStringList _strings; + int _groupMetric; + int _groupCount; + QString _groupHint; + int _metricCount; + int _version; + +public: + + ~Launch(); + + Launch(const QString &version = ""); // defaults to version 2.0 + Launch(const Launch &rhs); + const Launch &operator=(const Launch &rhs); + + static const char *launchPath(); + + void setDefaultOptions(int interval = 5, + int debug = 0, + const char *timeport = NULL, + const char *starttime = NULL, + const char *endtime = NULL, + const char *offset = NULL, + const char *timezone = NULL, + const char *defsourcetype = NULL, + const char *defsourcename = NULL, + bool selected = false); + + void addOption(const char *name, const char *value); + void addOption(const char *name, int value); + + void addMetric(const QmcMetric &metric, + const SbColor &color, + int instance, + bool useSocks = false); + + void addMetric(const QmcMetric &metric, + const ColorScale &scale, + int instance, + bool useSocks = false); + + void addMetric(int context, + const QString &source, + const QString &host, + const QString &metric, + const char *instance, + const QmcDesc &desc, + const SbColor &color, + double scale, + bool useSocks = false); + // Add metric with static color and scale + + void addMetric(int context, + const QString &source, + const QString &host, + const QString &metric, + const char *instance, + const QmcDesc &desc, + const ColorScale &scale, + bool useSocks = false); + // Add metric with dynamic color range + + void startGroup(const char *hint = "none"); + void endGroup(); + + void append(Launch const& rhs); + + void output(int fd) const; + + friend QTextStream& operator<<(QTextStream& os, Launch const& rhs); + +private: + + void preColor(int context, + const QString &source, + const QString &host, + const QString &metric, + bool useSocks, + QString &str); + void postColor(const QmcDesc &desc, const char *instance, QString &str); +}; + +#endif /* _LAUNCH_H_ */ diff --git a/src/pmview/lex.l b/src/pmview/lex.l new file mode 100644 index 0000000..40b9285 --- /dev/null +++ b/src/pmview/lex.l @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +%{ + +#ifdef FLEX_SCANNER + +int pmviewFlexInput (char * buf, int ms); + +#undef YY_INPUT +#define YY_INPUT(b,r,ms) (r=pmviewFlexInput(b, ms)) + +#ifdef __cplusplus +extern "C" { +#endif +int yylex (void); +#ifdef __cplusplus +} +#endif + +#else /* AT&T Lex */ + +#undef input +#undef unput +#undef yywrap + +char input(void); +void unput(char); + +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "viewobj.h" +#include "barobj.h" +#include "text.h" +#include "stackobj.h" +#include "gridobj.h" +#include "gram.h" + +extern void yywarn(const char *); +extern void yyerror(const char *); +extern FILE *theAltConfig; + +#ifdef YYLMAX +#undef YYLMAX +#endif + +#define YYLMAX 2048 + +#ifdef LEX_DEBUG +#define RETURN(c) if (pmDebug & DBG_TRACE_APPL2) { pmprintf("Lex: %d\n", c); pmflush (); } return c +#else +#define RETURN(c) if (theAltConfig) fprintf(theAltConfig, "%s", yytext); return c +#endif + +static void lexerror (const char * msg, const char * yytext); +%} + + +%% + +[-]?[0-9]+[\.][0-9]+ { + yylval.y_real = strtod(yytext, (char **)0); + RETURN(REAL); + } + +0[xX][0-9]+ { + yylval.y_int = (int)strtol(&yytext[2], (char **)0, 16); + RETURN(INT); + } + +[-]?[0-9]+ { + yylval.y_int = atoi(yytext); + RETURN(INT); + } + +[Pp][Mm][Vv][Ii][Ee][Ww][\t ]+[vV]ersion[\t ]+[0-9]+\.[0-9]+ { + yylval.y_str = strdup (yytext+15); + RETURN(PMVIEW); + } + +_grid { RETURN(GRID); } +_bar { RETURN(BAR); } +_labeledbar { RETURN(BAR); } +_stack { RETURN(STACK); } +_util { RETURN(UTIL); } +_metrics { RETURN(METRICLIST); } +_color[lL]ist { RETURN(COLORLIST); } +_color[sS]cale { RETURN(COLORSCALE); } +_label { RETURN(LABEL); } +_metric[lL]abels { RETURN(METRICLABEL); } +_inst[lL]abels { RETURN(INSTLABEL); } +_direction { RETURN(DIRECTION); } +_size { RETURN(SIZE); } +_text { RETURN(TEXT); } +_scale { RETURN(SCALE); } +_history { RETURN(HISTORY); } +_bar[lL]ength { RETURN(BAR_LENGTH); } +_bar[xX][mM]argin { RETURN(MARGIN_WIDTH); } +_bar[yY][mM]argin { RETURN(MARGIN_DEPTH); } +_margin[wW]idth { RETURN(MARGIN_WIDTH); } +_margin[dD]epth { RETURN(MARGIN_DEPTH); } +_bar[hH]eight { RETURN(BAR_HEIGHT); } +_bar[bB]ase[hH]eight { RETURN(BASE_HEIGHT); } +_base[hH]eight { RETURN(BASE_HEIGHT); } +_stack[lL]ength { RETURN(BAR_LENGTH); } +_stack[xX][mM]argin { RETURN(MARGIN_WIDTH); } +_stack[yY][mM]argin { RETURN(MARGIN_DEPTH); } +_stack[hH]eight { RETURN(BAR_HEIGHT); } +_stack[bB]ase[hH]eight { RETURN(BASE_HEIGHT); } +_grid[sS]pace { RETURN(GRID_SPACE); } +_grid[wW]idth { RETURN(GRID_WIDTH); } +_grid[dD]epth { RETURN(GRID_DEPTH); } +_grid[hH]eight { RETURN(BASE_HEIGHT); } +_grid[cC]olor { RETURN(BASE_COLOR); } +_base[cC]olor { RETURN(BASE_COLOR); } +_gap[wW]idth { RETURN(GAP_WIDTH); } +_gap[dD]epth { RETURN(GAP_DEPTH); } +_gap[lL]abel { RETURN(GAP_LABEL); } +_label[mM]argin { RETURN(LABEL_MARGIN); } +_label[cC]olor { RETURN(LABEL_COLOR); } +_stack[lL]abel { RETURN(STACK_LABEL); } +_base[lL]abel { RETURN(BASE_LABEL); } +_box { RETURN(BOX); } +_pipe[Ll]ength { RETURN(PIPE_LENGTH); } +_pipe { RETURN(PIPE); } +_pipe[Tt]ag { RETURN(PIPETAG); } +_link { RETURN(GR_LINK); } +_xing { RETURN(GR_XING); } +_sceneFile { RETURN(SCENE_FILE); } + +[_]?[nN] { yylval.y_align = ViewObj::north; RETURN(ALIGN); } +[_]?north { yylval.y_align = ViewObj::north; RETURN(ALIGN); } +[_]?[sS] { yylval.y_align = ViewObj::south; RETURN(ALIGN); } +[_]?south { yylval.y_align = ViewObj::south; RETURN(ALIGN); } +[_]?[eE] { yylval.y_align = ViewObj::east; RETURN(ALIGN); } +[_]?east { yylval.y_align = ViewObj::east; RETURN(ALIGN); } +[_]?[wW] { yylval.y_align = ViewObj::west; RETURN(ALIGN); } +[_]?west { yylval.y_align = ViewObj::west; RETURN(ALIGN); } +[_]?[nN][wW] { yylval.y_align = ViewObj::northWest; RETURN(ALIGN); } +[_]?northwest { yylval.y_align = ViewObj::northWest; RETURN(ALIGN); } +[_]?[nN][eE] { yylval.y_align = ViewObj::northEast; RETURN(ALIGN); } +[_]?northeast { yylval.y_align = ViewObj::northEast; RETURN(ALIGN); } +[_]?[sS][wW] { yylval.y_align = ViewObj::southWest; RETURN(ALIGN); } +[_]?southwest { yylval.y_align = ViewObj::southWest; RETURN(ALIGN); } +[_]?[sS][eE] { yylval.y_align = ViewObj::southEast; RETURN(ALIGN); } +[_]?southeast { yylval.y_align = ViewObj::southEast; RETURN(ALIGN); } +[_]?center { yylval.y_align = ViewObj::center; RETURN(ALIGN); } + +[_]?up { yylval.y_textDir = Text::up; RETURN(DIRVAL); } +[_]?down { yylval.y_textDir = Text::down; RETURN(DIRVAL); } +[_]?left { yylval.y_textDir = Text::left; RETURN(DIRVAL); } +[_]?right { yylval.y_textDir = Text::right; RETURN(DIRVAL); } + +[_]?small { yylval.y_fontSize = Text::small; RETURN(SIZ); } +[_]?medium { yylval.y_fontSize = Text::medium; RETURN(SIZ); } +[_]?large { yylval.y_fontSize = Text::large; RETURN(SIZ); } + +[_]?show { yylval.y_bool = true; RETURN(BOOL); } +[_]?hide { yylval.y_bool = false; RETURN(BOOL); } + +[_]?towards { yylval.y_labelDir = BarObj::towards; RETURN(LABEL_DIR); } +[_]?away { yylval.y_labelDir = BarObj::away; RETURN(LABEL_DIR); } + +[_]?row { yylval.y_instDir = BarMod::instPerRow; RETURN(INST_DIR); } +[_]?col { yylval.y_instDir = BarMod::instPerCol; RETURN(INST_DIR); } + +_cube { yylval.y_shape = ViewObj::cube; RETURN(SHAPE); } +_cylinder { yylval.y_shape = ViewObj::cylinder; RETURN(SHAPE); } + +_[yY]mod { yylval.y_barMod = BarMod::yScale; RETURN(BAR_TYPE); } +_color[mM]od { yylval.y_barMod = BarMod::color; RETURN(BAR_TYPE); } +_color[yY][mM]od { yylval.y_barMod = BarMod::colYScale; RETURN(BAR_TYPE); } + +_util[mM]od { yylval.y_stackMod = StackMod::util; RETURN(STACK_TYPE); } +_fill[mM]od { yylval.y_stackMod = StackMod::fixed; RETURN(STACK_TYPE); } + +_group[bB]y[rR]ow { yylval.y_barGroup = BarMod::groupByRow; RETURN(GROUP); } +_group[bB]y[cC]ol { yylval.y_barGroup = BarMod::groupByCol; RETURN(GROUP); } +_group[bB]y[mM]etric { yylval.y_barGroup = BarMod::groupByMetric; RETURN(GROUP); } +_group[bB]y[iI]nst { yylval.y_barGroup = BarMod::groupByInst; RETURN(GROUP); } + +[A-Za-z0-9_./\-,:?=+&]+(\[[ \t]*((\"[^\"\n]*\"|[^\"\n\]]*)[ \t]*\,?[ \t]*)+[ \t]*\]) { + /* + * Metric could be in the form: name + quoted instance or + * name + non-quoted instance (i,e no " between the [ ] ) or + * just name with no instance spec. Name with no instance spec + * is delegated to the NAME token, the rest is handled here. + */ + yylval.y_str = strdup (yytext); + RETURN(METRIC); + } + +[A-Za-z0-9_\.\/\-\,:\?\=\+\&]+ { + /* + * While parsing, NAME token can be used both in general context + * or as name for METRIC. I don't want to do context-specific + * lexical analisys, so NAME is restricted to the list of chars + * above and it cannot have opening or closing square brackets + */ + yylval.y_str = strdup (yytext); + RETURN(NAME); + } + +\"[^\"\n][^\"\n]*\" { + yylval.y_str = (char *)malloc(yyleng-1); + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + RETURN(STRING); + } + +\"[^\"\n][^\"\n]*\n { + lexerror("Expected closing \"", yytext); + /*NOTREACHED*/ + } + +\( { RETURN(OPENB); } +\) { RETURN(CLOSEB); } + +\#.*\n {} +\n { if (theAltConfig) fputc('\n', theAltConfig); } + +[\t ]+ { if (theAltConfig) fputc(' ', theAltConfig); } + +. { + sprintf(theBuffer, "Illegal character '%c'", yytext[0]); + lexerror(theBuffer, yytext); + /*NOTREACHED*/ + } +%% + +static char *line; +static int linepos; +static int linelen; +static int mark = -1; + +extern int lineNum; + +#ifdef FLEX_SCANNER +static int in = '\0'; + +int yywrap(void) +{ + return in == EOF; +} + +int +pmviewFlexInput(char *buf, int ms) +{ + if (in == '\n') { + lineNum++; + linepos=0; + } + + if (linepos >= linelen - 1) { + if (linelen == 0) + linelen = 128; + else + linelen *= 2; + line = (char *)realloc(line, linelen * sizeof(char)); + } + + if (ms > 0) { + if ((in = fgetc(yyin)) != EOF) { + line[linepos++] = buf[0] = in & 0xFFU; + ms = 1; + } else { + ms = 0; + } + } + + return ms; +} + +void +skipAhead(void) +{ + while ((in != '\n') && (in != EOF)) { + char c; + pmviewFlexInput(&c, 1); + } +} + +char +input(void) +{ + char c; + if (pmviewFlexInput(&c, 1)) + return c; + return 0; +} + +char +lastinput(void) +{ + return (in & 0xFFU); +} + +#else + +static char lastc = 'A'; +static char peekc = '\0'; + +extern int lineNum; + +extern FILE *yyin; + +int +yywrap(void) +{ + return lastc == '\0'; +} + +char +input(void) +{ + int get; + + if (peekc != '\0') { + lastc = peekc; + peekc = '\0'; + return lastc; + } + + if (lastc == '\n') { + lineNum++; + linepos = 0; + } + else if (lastc == '\0') { + linepos = 0; + return lastc; + } + + if (linepos >= linelen - 1) { + if (linelen == 0) + linelen = 128; + else + linelen *= 2; + line = (char *)realloc(line, linelen * sizeof(char)); + } + + get = fgetc(yyin); + + if (get == EOF) + lastc = '\0'; + else + lastc = get; + + line[linepos++] = (char)get; + + return lastc; +} + +void +unput(char c) +{ + peekc = c; +} + +char +lastinput(void) +{ + return lastc; +} + +#endif + +int +markpos(void) +{ + mark = linepos; + return mark; +} + +int +locateError(void) +{ + int i; + + if (mark < 0) { + pmprintf("%s: Unrecoverable internal error in locateError()\n", + pmProgname); + pmflush(); + exit(1); + } + + if (feof(yyin)) { + return 0; + } + + for (i = 0; i < mark; i++) + if (line[i] == '\n' || line[i] == '\0') + break; + else + pmprintf("%c", line[i]); + pmprintf("\n"); + + for (i = 0; i < mark - 1; i++) + if (line[i] == '\n' || line[i] == '\0') + break; + else if (line[i] == '\t') + pmprintf("\t"); + else + pmprintf(" "); + + pmprintf("^ at or near here\n"); + + return 1; +} + +/* This is 'die now' version of yyerror - static and only used for errors + * in lexer, i.e those, where we've got no reasonable chance to do any + * recovery. */ +static void +lexerror(const char * msg, const char * yytext) +{ + char *pos; + + if ((pos = strstr(line, yytext)) != NULL) { + int i; + + for (i = 0; (line[i] != '\n') && (line[i] != '\0'); i++) + pmprintf("%c", line[i]); + pmprintf("\n"); + + for (i = 0; line+i != pos; i++) { + if (line[i] == '\n' || line[i] == '\0') + break; + else if (line[i] == '\t') + pmprintf("\t"); + else + pmprintf(" "); + } + pmprintf("^ at or near here\n"); + } + + pmprintf("%s: Fatal error in configuration file at line %d:\n\t%s\n", + pmProgname, lineNum + 1, msg); + + pmflush(); + exit(1); +} diff --git a/src/pmview/link.cpp b/src/pmview/link.cpp new file mode 100644 index 0000000..bddfb0c --- /dev/null +++ b/src/pmview/link.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoRotationXYZ.h> +#include <Inventor/nodes/SoCylinder.h> +#include <Inventor/nodes/SoSphere.h> +#include <Inventor/nodes/SoBaseColor.h> + +#include "togglemod.h" +#include "link.h" +#include "defaultobj.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Constructor for the Link. + * Note that we use different alignment for the base than the one + * for the scene. Base is always centered and we get the visual + * effect by addjusting the height of the cylinder(s). + \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +Link::Link (const DefaultObj & defs, + int c, int r, + int colSpan, int rowSpan, + Alignment a) + : BaseObj (false, defs, c, r, colSpan, rowSpan, center) + , _tag ("\n") +{ + _objtype |= LINK; + + for ( int i=0; i < 3; i++) { + _color[i] = defs.baseColor(i); + } + + align = a; + c1 = c2 = 0; + cellHeight = defs.baseHeight(); + cellDepth = defs.baseHeight(); + cellWidth = defs.baseHeight(); + + _width = colSpan * cellWidth; + _depth = rowSpan * cellDepth; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void +Link::finishedAdd () +{ + BaseObj::addBase (_root); + + SoBaseColor * color = new SoBaseColor; + color->rgb.setValue (_color); + _root->addChild (color); + + float radius = cellHeight * 0.4; + + SoSeparator * mainsep = new SoSeparator; + + SoTranslation * toCenter = new SoTranslation; + toCenter->translation.setValue (_width/2.0, cellHeight/2.0, _depth/2.0); + mainsep->addChild (toCenter); + + if ( align == northEast || align == northWest || + align == southEast || align == southWest ) { + SoRotationXYZ * mrot = new SoRotationXYZ; + mrot->axis = SoRotationXYZ::Y; + switch (align) { + case northEast: + mrot->angle = 0; + break; + + case northWest: + mrot->angle = M_PI/2; + break; + + case southWest: + mrot->angle = M_PI; + break; + + case southEast: + mrot->angle = -(M_PI/2); + break; + + default: + break; + } + mainsep->addChild (mrot); + + + // L-shape link - could leave without a separator, but + // geometry calculation becomes messy, so just add one. + SoSeparator * sp = new SoSeparator; + + SoSphere * s = new SoSphere; + s->radius.setValue (radius); + sp->addChild (s); + + t2 = new SoTranslation; + t3 = new SoTranslation; + + c1 = new SoCylinder; + c1->radius.setValue (radius); + + c2 = new SoCylinder; + c2->radius.setValue (radius); + + if ( align == northEast || align == southWest ) { + c1->height.setValue (_depth/2.0); + c2->height.setValue (_width/2.0); + + t2->translation.setValue (0, 0, _depth/-4.0); + t3->translation.setValue (_width/4.0, _depth/4.0, 0); + } else { + c1->height.setValue (_width/2.0); + c2->height.setValue (_depth/2.0); + + t2->translation.setValue (0, 0, _width/-4.0); + t3->translation.setValue (_depth/4.0, _width/4.0, 0); + } + + sp->addChild (t2); + + SoRotationXYZ * r1 = new SoRotationXYZ; + r1->axis = SoRotationXYZ::X; + r1->angle = M_PI/2; + + sp->addChild (r1); + sp->addChild (c1); + sp->addChild (t3); + + SoRotationXYZ * r2 = new SoRotationXYZ; + r2->axis = SoRotationXYZ::Z; + r2->angle = M_PI/2; + + sp->addChild (r2); + sp->addChild (c2); + + mainsep->addChild (sp); + } else { + SoRotationXYZ * r = new SoRotationXYZ; + + int h; + + if ( align == north || align == south ) { // Vertical pipe + h = rows() * cellDepth; + + r->axis = SoRotationXYZ::X; + r->angle = M_PI/2; + } else { // Horizontal pipe + h = cols() * cellWidth; + + r->axis = SoRotationXYZ::Z; + r->angle = M_PI/2; + } + + mainsep->addChild (r); + + c1 = new SoCylinder; + c1->radius.setValue (radius); + c1->height.setValue (h); + mainsep->addChild (c1); + } + + + ToggleMod * m = new ToggleMod (mainsep, (const char *)_tag.toAscii()); + _root->addChild (m->root()); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Adjust the size of the link. Note, that we don't need to change the + * reported width and depth of an object (Grid does not cope with such + * changes very well) just adjust the length of cylinder(s). +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void +Link::setTran (float x, float z, int w, int d) +{ + if ( c1 && c2 ) { + if ( align == northEast || align == southWest ) { + c1->height.setValue (d/2.0); + c2->height.setValue (w/2.0); + + t2->translation.setValue (0, 0, d/-4.0); + t3->translation.setValue (w/4.0, d/4.0, 0); + } else { + c1->height.setValue (w/2.0); + c2->height.setValue (d/2.0); + + t2->translation.setValue (0, 0, w/-4.0); + t3->translation.setValue (d/4.0, w/4.0, 0); + } + } else { + c1->height.setValue ((align == north || align == south ) ? d : w ); + } + + BaseObj::setTran (x, z, w, d); +} + +void +Link::setTag (const char * s) +{ + _tag = s; + + if ( strchr (s, '\n') == NULL ) { + _tag.append ("\n"); + } +} diff --git a/src/pmview/link.h b/src/pmview/link.h new file mode 100644 index 0000000..dc4270e --- /dev/null +++ b/src/pmview/link.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _LINK_H +#define _LINK_H + +#include "baseobj.h" + +class SoCylinder; + +class Link : public BaseObj +{ +public: + Link (const DefaultObj &, int, int, int, int, Alignment); + + void finishedAdd (); + void setTran (float, float, int, int); + int width () const { return _width; } + int depth () const { return _depth; } + const char * name () const { return "Link"; } + void setTag (const char *); + + virtual Modulate *modObj() { return 0; } + +private: + Link(); + Link (Link const &); + Link const& operator=(Link const &); + + int cellWidth, cellHeight, cellDepth; + int _width, _depth; + Alignment align; + + SoCylinder * c1, * c2; + SoTranslation * t2, * t3; + QString _tag; + + float _color[3]; +}; + +#endif diff --git a/src/pmview/main.cpp b/src/pmview/main.cpp new file mode 100644 index 0000000..115a90b --- /dev/null +++ b/src/pmview/main.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2009, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <QtCore/QSettings> +#include <QtCore/QTextStream> +#include <QtGui/QMessageBox> +#include <Inventor/nodes/SoCube.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include "qed_fileiconprovider.h" +#include "qed_timecontrol.h" +#include "qed_console.h" +#include "scenegroup.h" +#include "pcpcolor.h" +#include "modlist.h" +#include "viewobj.h" +#include "main.h" + +#include <iostream> +using namespace std; + +int Cflag; +int Lflag; +int Wflag; +char *outgeometry; +Settings globalSettings; + +float theScale = 1.0; +const int theBufferLen = 2048; +char theBuffer[theBufferLen]; + +// Globals used to provide single instances of classes used across pmview +SceneGroup *liveGroup; // one metrics class group for all hosts +SceneGroup *archiveGroup; // one metrics class group for all archives +SceneGroup *activeGroup; // currently active metric fetchgroup +QedTimeControl *pmtime; // one timecontrol class for pmtime +PmView *pmview; + +static const char *options = "A:a:Cc:D:g:h:Ln:O:p:S:T:t:VzZ:?"; + +static void usage(void) +{ + pmprintf("Usage: %s [options] [sources]\n\n" +"Options:\n" +" -A align align sample times on natural boundaries\n" +" -a archive add PCP log archive to metrics source list\n" +" -c configfile initial view to load\n" +" -C with -c, parse config, report any errors and exit\n" +" -CC like -C, but also connect to pmcd to check semantics\n" +" -g geometry image geometry Width x Height (WxH)\n" +" -h host add host to list of live metrics sources\n" +" -L directly fetch metrics from localhost, PMCD is not used\n" +" -O offset initial offset into the time window\n" +" -p port port name for connection to existing time control\n" +" -S starttime start of the time window\n" +" -T endtime end of the time window\n" +" -t interval sample interval [default: %d seconds]\n" +" -V display pmview version number and exit\n" +" -Z timezone set reporting timezone\n" +" -z set reporting timezone to local time of metrics source\n", + pmProgname, (int)PmView::defaultViewDelta()); + pmflush(); + exit(1); +} + +int warningMsg(const char *file, int line, const char *msg, ...) +{ + int sts = QMessageBox::Ok; + va_list arg; + va_start(arg, msg); + + int pos = sprintf(theBuffer, "%s: Warning: ", pmProgname); + pos += vsprintf(theBuffer + pos, msg, arg); + sprintf(theBuffer+pos, "\n"); + + if (pmDebug) { + QTextStream cerr(stderr); + cerr << file << ":" << line << ": " << theBuffer << endl; + } + + sts = QMessageBox::warning(pmview, "Warning", theBuffer, sts, sts); + va_end(arg); + + return sts; +} + +double QmcTime::secondsToUnits(double value, QmcTime::DeltaUnits units) +{ + switch (units) { + case Milliseconds: + value = value * 1000.0; + break; + case Minutes: + value = value / 60.0; + break; + case Hours: + value = value / (60.0 * 60.0); + break; + case Days: + value = value / (60.0 * 60.0 * 24.0); + break; + case Weeks: + value = value / (60.0 * 60.0 * 24.0 * 7.0); + break; + case Seconds: + default: + break; + } + return value; +} + +double QmcTime::deltaValue(QString delta, QmcTime::DeltaUnits units) +{ + return QmcTime::secondsToUnits(delta.trimmed().toDouble(), units); +} + +QString QmcTime::deltaString(double value, QmcTime::DeltaUnits units) +{ + QString delta; + + value = QmcTime::secondsToUnits(value, units); + if ((double)(int)value == value) + delta.sprintf("%.2f", value); + else + delta.sprintf("%.6f", value); + return delta; +} + +void writeSettings(void) +{ + QSettings userSettings; + + userSettings.beginGroup(pmProgname); + if (globalSettings.viewDeltaModified) { + globalSettings.viewDeltaModified = false; + userSettings.setValue("viewDelta", globalSettings.viewDelta); + } + if (globalSettings.loggerDeltaModified) { + globalSettings.loggerDeltaModified = false; + userSettings.setValue("loggerDelta", globalSettings.loggerDelta); + } + if (globalSettings.viewBackgroundModified) { + globalSettings.viewBackgroundModified = false; + userSettings.setValue("viewBackgroundColor", + globalSettings.viewBackgroundName); + } + if (globalSettings.viewHighlightModified) { + globalSettings.viewHighlightModified = false; + userSettings.setValue("viewHighlightColor", + globalSettings.viewHighlightName); + } + if (globalSettings.gridBackgroundModified) { + globalSettings.gridBackgroundModified = false; + userSettings.setValue("gridBackgroundColor", + globalSettings.gridBackgroundName); + } + if (globalSettings.initialToolbarModified) { + globalSettings.initialToolbarModified = false; + userSettings.setValue("initialToolbar", globalSettings.initialToolbar); + } + if (globalSettings.nativeToolbarModified) { + globalSettings.nativeToolbarModified = false; + userSettings.setValue("nativeToolbar", globalSettings.nativeToolbar); + } + if (globalSettings.toolbarLocationModified) { + globalSettings.toolbarLocationModified = false; + userSettings.setValue("toolbarLocation", + globalSettings.toolbarLocation); + } + if (globalSettings.toolbarActionsModified) { + globalSettings.toolbarActionsModified = false; + userSettings.setValue("toolbarActions", globalSettings.toolbarActions); + } + userSettings.endGroup(); +} + +void readSettings(void) +{ + QSettings userSettings; + userSettings.beginGroup(pmProgname); + + // + // Parameters related to sampling + // + globalSettings.viewDeltaModified = false; + globalSettings.viewDelta = userSettings.value("viewDelta", + PmView::defaultViewDelta()).toDouble(); + globalSettings.loggerDeltaModified = false; + globalSettings.loggerDelta = userSettings.value("loggerDelta", + PmView::defaultLoggerDelta()).toDouble(); + + // + // Everything colour related + // + globalSettings.viewBackgroundModified = false; + globalSettings.viewBackgroundName = userSettings.value( + "viewBackgroundColor", "black").toString(); + globalSettings.viewBackground = QColor(globalSettings.viewBackgroundName); + + globalSettings.viewHighlightModified = false; + globalSettings.viewHighlightName = userSettings.value( + "viewHighlightColor", "white").toString(); + globalSettings.viewHighlight = QColor(globalSettings.viewHighlightName); + + globalSettings.gridBackgroundModified = false; + globalSettings.gridBackgroundName = userSettings.value( + "gridBackgroundColor", "rgbi:0.15/0.15/0.15").toString(); + globalSettings.gridBackground = QColor(globalSettings.gridBackgroundName); + + // + // Toolbar user preferences + // + globalSettings.initialToolbarModified = false; + globalSettings.initialToolbar = userSettings.value( + "initialToolbar", 1).toInt(); + globalSettings.nativeToolbarModified = false; + globalSettings.nativeToolbar = userSettings.value( + "nativeToolbar", 1).toInt(); + globalSettings.toolbarLocationModified = false; + globalSettings.toolbarLocation = userSettings.value( + "toolbarLocation", 0).toInt(); + QStringList actionList; + globalSettings.toolbarActionsModified = false; + if (userSettings.contains("toolbarActions") == true) + globalSettings.toolbarActions = + userSettings.value("toolbarActions").toStringList(); + // else: (defaults come from the pmview.ui interface specification) + + userSettings.endGroup(); +} + +int +genInventor(void) +{ + int sts = 0; + const char *configfile; + QTextStream cerr(stderr); + + if (theConfigName.length()) { + configfile = strdup((const char *)theConfigName.toAscii()); + if (!(yyin = fopen(configfile, "r"))) { + pmprintf( + "%s: Error: Unable to open configuration file \"%s\": %s\n", + pmProgname, configfile, strerror(errno)); + return -1; + } + theAltConfigName = theConfigName; + } else { + configfile = mktemp(strdup("/tmp/pmview.XXXXXX")); + if (pmDebug & DBG_TRACE_APPL0) + cerr << "genInventor: Copy of configuration saved to " + << configfile << endl; + if (!(theAltConfig = fopen(configfile, "w"))) { + pmprintf("%s: Warning: Unable to save configuration for " + "recording to \"%s\": %s\n", + pmProgname, configfile, strerror(errno)); + } + theAltConfigName = configfile; + } + + yyparse(); + + if (theAltConfig) + fclose(theAltConfig); + + if (pmDebug & DBG_TRACE_APPL0) { + cerr << pmProgname << ": " << errorCount << " errors detected in " + << theConfigName << endl; + } + + sts = -errorCount; + + if (rootObj != NULL) { + rootObj->setTran(0, 0, rootObj->width(), rootObj->depth()); + + SoSeparator *sep = new SoSeparator; + SoTranslation *tran = new SoTranslation(); + tran->translation.setValue(rootObj->width() / -2.0, 0.0, + rootObj->depth() / -2.0); + sep->addChild(tran); + + if (pmDebug & DBG_TRACE_APPL0 || + pmDebug & DBG_TRACE_APPL1 || + pmDebug & DBG_TRACE_APPL2) { + SoBaseColor *col = new SoBaseColor; + col->rgb.setValue(1.0, 0.0, 0.0); + sep->addChild(col); + SoCube * cube = new SoCube; + cube->width = 10; + cube->depth = 5.0; + cube->height = 25; + sep->addChild(cube); + } + + sep->addChild(rootObj->root()); + theModList->setRoot(sep); + } + + if ((ViewObj::numModObjects() == 0 || theModList->size() == 0) && + elementalNodeList.getLength() == 0) { + pmprintf("%s: No valid modulated objects in the scene\n", + pmProgname); + sts--; + } + else if (sts < 0) { + pmprintf("%s: Unrecoverable errors in the configuration file %s\n", + pmProgname, (const char *)theConfigName.toAscii()); + } + + return sts; +} + +int +main(int argc, char **argv) +{ + int c, sts; + int errflg = 0; + char *msg; + + QedApp a(argc, argv); + readSettings(); + + liveGroup = new SceneGroup(); + archiveGroup = new SceneGroup(); + + while ((c = a.getopts(options)) != EOF) { + switch (c) { + + case 'C': + Cflag++; + break; + + case 'g': + outgeometry = optarg; + break; + + case 'c': + theConfigName = optarg; + break; + + case '?': + default: + usage(); + } + } + + a.startconsole(); + + if (a.my.archives.size() > 0) + while (optind < argc) + a.my.archives.append(argv[optind++]); + else + while (optind < argc) + a.my.hosts.append(argv[optind++]); + + if (optind != argc) + errflg++; + if (errflg) + usage(); + + if (a.my.pmnsfile && (sts = pmLoadNameSpace(a.my.pmnsfile)) < 0) { + pmprintf("%s: %s\n", pmProgname, pmErrStr(sts)); + pmflush(); + exit(1); + } + + // Create all of the sources + if (a.my.Lflag) { + liveGroup->use(PM_CONTEXT_LOCAL, QmcSource::localHost); + Lflag = 1; + } + for (c = 0; c < a.my.hosts.size(); c++) { + if (liveGroup->use(PM_CONTEXT_HOST, a.my.hosts[c]) < 0) + a.my.hosts.removeAt(c); + } + for (c = 0; c < a.my.archives.size(); c++) { + if (archiveGroup->use(PM_CONTEXT_ARCHIVE, a.my.archives[c]) < 0) + a.my.archives.removeAt(c); + } + if (!a.my.Lflag && a.my.hosts.size() == 0 && a.my.archives.size() == 0) + liveGroup->createLocalContext(); + pmflush(); + console->post("Metric group setup complete (%d hosts, %d archives)", + a.my.hosts.size(), a.my.archives.size()); + + if (a.my.zflag) { + if (a.my.archives.size() > 0) + archiveGroup->useTZ(); + if (a.my.hosts.size() > 0) + liveGroup->useTZ(); + } + else if (a.my.tz != NULL) { + if (a.my.archives.size() > 0) + archiveGroup->useTZ(QString(a.my.tz)); + if (a.my.hosts.size() > 0) + liveGroup->useTZ(QString(a.my.tz)); + if ((sts = pmNewZone(a.my.tz)) < 0) { + pmprintf("%s: cannot set timezone to \"%s\": %s\n", + pmProgname, (char *)a.my.tz, pmErrStr(sts)); + pmflush(); + exit(1); + } + } + + // + // Choose which View will be displayed initially - archive/live. + // If any archives given on command line, we go Archive mode; + // otherwise Live mode wins. Our initial pmtime connection is + // set in that mode too. Later we'll make a second connection + // in the other mode (and only "on-demand"). + // + if (a.my.archives.size() > 0) { + activeGroup = archiveGroup; + archiveGroup->defaultTZ(a.my.tzLabel, a.my.tzString); + archiveGroup->updateBounds(); + a.my.logStartTime = archiveGroup->logStart(); + a.my.logEndTime = archiveGroup->logEnd(); + if ((sts = pmParseTimeWindow(a.my.Sflag, a.my.Tflag, + a.my.Aflag, a.my.Oflag, + &a.my.logStartTime, &a.my.logEndTime, + &a.my.realStartTime, &a.my.realEndTime, + &a.my.position, &msg)) < 0) { + pmprintf("Cannot parse archive time window\n%s\n", msg); + free(msg); + usage(); + } + } + else { + activeGroup = liveGroup; + liveGroup->defaultTZ(a.my.tzLabel, a.my.tzString); + gettimeofday(&a.my.logStartTime, NULL); + a.my.logEndTime.tv_sec = a.my.logEndTime.tv_usec = INT_MAX; + if ((sts = pmParseTimeWindow(a.my.Sflag, a.my.Tflag, + a.my.Aflag, a.my.Oflag, + &a.my.logStartTime, &a.my.logEndTime, + &a.my.realStartTime, &a.my.realEndTime, + &a.my.position, &msg)) < 0) { + pmprintf("Cannot parse live time window\n%s\n", msg); + free(msg); + usage(); + } + } + console->post("Timezones and time window setup complete"); + + pmview = new PmView; + pmtime = new QedTimeControl; + fileIconProvider = new QedFileIconProvider(); + console->post("Phase1 user interface constructors complete"); + + // Start pmtime process for time management + pmtime->init(a.my.port, a.my.archives.size() == 0, &a.my.delta, + &a.my.position, &a.my.realStartTime, &a.my.realEndTime, + a.my.tzString, a.my.tzLabel); + + pmview->init(); + liveGroup->init(pmtime->liveInterval(), pmtime->livePosition()); + archiveGroup->init(pmtime->archiveInterval(), pmtime->archivePosition()); + console->post("Phase2 user interface setup complete"); + + PCPColor::initClass(); + theModList = new ModList(pmview->viewer(), &PmView::selectionCB, NULL, NULL); + if (genInventor() < 0) { + pmflush(); + exit(1); + } + + if (Cflag) // done with -c config, quit + return 0; + + if (pmview->view(false, 0, 1, 0, M_PI / 4.0, theGlobalScale) == false) { + pmflush(); + exit(1); + } + + pmview->viewer()->viewAll(); + pmview->enableUi(); + pmview->show(); + console->post("Top level window shown"); + + a.connect(&a, SIGNAL(lastWindowClosed()), pmview, SLOT(quit())); + return a.exec(); +} diff --git a/src/pmview/main.h b/src/pmview/main.h new file mode 100644 index 0000000..ccd66b0 --- /dev/null +++ b/src/pmview/main.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2009, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef MAIN_H +#define MAIN_H + +#include "pcp/pmapi.h" +#include "pcp/impl.h" + +#include "qed_app.h" +#include "main.h" +#include "viewobj.h" +#include "colorlist.h" +#include "pmview.h" + +class QedApp; +class View; +class ModList; +class SoQtExaminerViewer; + +typedef void (*TermCB)(int); + +typedef struct { + // Sampling + double viewDelta; + bool viewDeltaModified; + double loggerDelta; + bool loggerDeltaModified; + + // Colors + QColor viewBackground; + QString viewBackgroundName; + bool viewBackgroundModified; + QColor viewHighlight; + QString viewHighlightName; + bool viewHighlightModified; + QColor gridBackground; + QString gridBackgroundName; + bool gridBackgroundModified; + + // Toolbar + int initialToolbar; + bool initialToolbarModified; + int nativeToolbar; + bool nativeToolbarModified; + int toolbarLocation; + int toolbarLocationModified; + QStringList toolbarActions; + bool toolbarActionsModified; +} Settings; + +// TODO: old X-resources... +// ! Background color of read-only labels +// !PmView+*readOnlyBackground: Black +// ! Maximum value before saturation +// ! The default of 1.05 allows for 5% error in the time delta when +// ! determining rates, before values are deemed saturated. +// PmView+*saturation: 1.05 +// ! Use fast anti-aliasing +// PmView+*antiAliasSmooth: tree +// ! Number of anti-aliasing passes: 1-255. Only 1 pass disables antialiasing. +// PmView+*antiAliasPasses: 1 +// ! Grid, Bar and Stack object base borders +// PmView+*baseBorderWidth: 8 +// PmView+*baseBorderDepth: 8 +// ! Height of Grid, Bar and Stack bases +// PmView+*baseHeight: 2 +// ! Color of base plane +// PmView+*baseColor: rgbi:0.15/0.15/0.15 +// ! Spacing between Bar blocks +// PmView+*barSpaceWidth: 8 +// PmView+*barSpaceDepth: 8 +// ! Spacing between Bar base and labels +// PmView+*barSpaceLabel: 6 +// ! Width and depth of Bar blocks +// PmView+*barLength: 28 +// PmView+*barHeight: 80 +// ! Margin around a Label +// PmView+*labelMargin: 5 +// ! Color of labels +// PmView+*labelColor: rgbi:1.0/1.0/1.0 +// ! Width and depth of Grid columns and rows +// PmView+*gridMinWidth: 20 +// PmView+*gridMinDepth: 20 + +extern Settings globalSettings; +extern void readSettings(); +extern void writeSettings(); +extern QColor nextColor(const QString &, int *); + +extern int Cflag; +extern int Lflag; +extern char *outgeometry; + +extern QString theConfigName; // Configuration file name +extern FILE *theConfigFile; // Configuration file +extern ColorList theColorLists; // ColorLists generated while parsing config +extern float theGlobalScale; // Scale applied to entire scene +extern FILE *theAltConfig; // Save the config file here +extern bool theAltConfigFlag; // True when config is saved to temporary file +extern QString theAltConfigName; // Name of the saved configuration file + +class SceneGroup; +extern SceneGroup *liveGroup; +extern SceneGroup *archiveGroup; +extern SceneGroup *activeGroup; + +class PmView; +extern PmView *pmview; + +class QedTimeControl; +extern QedTimeControl *pmtime; + +extern int genInventor(); +extern char lastinput(); +extern char input(); +extern int markpos(); +extern int locateError(); + +extern ViewObj *rootObj; +extern int errorCount; +extern int yyparse(void); +extern FILE *yyin; + +extern float theScale; // The scale controls multiplier +extern ModList *theModList; // List of modulated objects +extern View *theView; // Viewer coordinator +extern QedApp *theApp; // Our application object +extern const int theBufferLen; // Length of theBuffer +extern char theBuffer[]; // String buffer for anything +extern const QString theDefaultFlags; + +int setup(const char *appname, int *argc, char **argv, + void *cmdopts, int numOpts, TermCB termCB); + +#define _POS_ __FILE__, __LINE__ + +int warningMsg(const char *fileName, int line, const char *msg, ...); +int errorMsg(const char *fileName, int line, const char *msg, ...); +int fatalMsg(const char *fileName, int line, const char *msg, ...); + +#endif // MAIN_H diff --git a/src/pmview/metriclist.cpp b/src/pmview/metriclist.cpp new file mode 100644 index 0000000..fed2e40 --- /dev/null +++ b/src/pmview/metriclist.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "metriclist.h" +#include "scenegroup.h" +#include "main.h" + +static bool doMetricFlag = true; +static MetricList *currentList; + +MetricList::~MetricList() +{ + int i; + + for (i = 0; i < _metrics.size(); i++) + delete _metrics[i]; + for (i = 0; i < _colors.size(); i++) + delete _colors[i]; +} + +MetricList::MetricList() +: _metrics(), _colors(), _values(0) +{ +} + +void +MetricList::toString(const SbColor &color, QString &str) +{ + char buf[48]; + + const float *values = color.getValue(); + sprintf(buf, "rgbi:%f/%f/%f", values[0], values[1], values[2]); + str = buf; +} + +int +MetricList::add(char const* metric, double scale) +{ + QmcMetric *ptr = new QmcMetric(activeGroup, metric, scale); + + if (ptr->status() >= 0) { + _metrics.append(ptr); + _values += ptr->numValues(); + } + + return ptr->status(); +} + +int +MetricList::add(char const* metric, double scale, int history) +{ + QmcMetric *ptr = new QmcMetric(activeGroup, metric, scale, history); + + if (ptr->status() >= 0) { + _metrics.append(ptr); + _values += ptr->numValues(); + } + + return ptr->status(); +} + +int +MetricList::add(pmMetricSpec *metric, double scale) +{ + QmcMetric *ptr = new QmcMetric(activeGroup, metric, scale); + + if (ptr->status() >= 0) { + _metrics.append(ptr); + _values += ptr->numValues(); + } + + return ptr->status(); +} + +int +MetricList::add(pmMetricSpec *metric, double scale, int history) +{ + QmcMetric *ptr = new QmcMetric(activeGroup, metric, scale, history); + + if (ptr->status() >= 0) { + _metrics.append(ptr); + _values += ptr->numValues(); + } + + return ptr->status(); +} + +void +MetricList::add(SbColor const& color) +{ + SbColor *ptr = new SbColor; + + ptr->setValue(color.getValue()); + _colors.append(ptr); +} + +void +MetricList::add(int packedcol) +{ + float tran = 0.0; + SbColor *ptr = new SbColor; + + ptr->setPackedValue(packedcol, tran); + _colors.append(ptr); +} + +void +MetricList::resolveColors(AlignColor align) +{ + int need = 0; + + switch(align) { + case perMetric: + need = _metrics.size(); + break; + case perValue: + need = _values; + break; + case noColors: + default: + need = 0; + break; + } + + if (_metrics.size() == 0) + return; + + if (_colors.size() == 0 && need > 0) + add(SbColor(0.0, 0.0, 1.0)); + + if (_colors.size() < need) { + int o = 0; + int n = 0; + + while (n < need) { + for (o = 0; o < _colors.size() && n < need; o++, n++) { + SbColor *ptr = new SbColor; + ptr->setValue(((SbColor *)_colors[o])->getValue()); + _colors.append(ptr); + } + } + } +} + +QTextStream& +operator<<(QTextStream &os, MetricList const &list) +{ + int i; + float r, g, b; + + for (i = 0; i < list._metrics.size(); i++) { + os << '[' << i << "]: "; + if (i < list._colors.size()) { + list._colors[i]->getValue(r, g, b); + os << r << ',' << g << ',' << b << ": "; + } + os << *(list._metrics[i]) << endl; + } + return os; +} + +static void +dometric(const char *name) +{ + if (currentList->add(name, 0.0) < 0) + doMetricFlag = false; +} + +int +MetricList::traverse(const char *str) +{ + pmMetricSpec *theMetric; + QString source; + char *msg; + int type = PM_CONTEXT_HOST; + SceneGroup *group = liveGroup; + int sts = 0; + + sts = pmParseMetricSpec((char *)str, 0, (char *)0, &theMetric, &msg); + if (sts < 0) { + pmprintf("%s: Error: Unable to parse metric spec:\n%s\n", + pmProgname, msg); + free(msg); + return sts; + /*NOTREACHED*/ + } + + // If the metric has instances, then it cannot be traversed + if (theMetric->ninst) { + sts = add(theMetric, 0.0); + } + else { + if (theMetric->isarch == 2) { + type = PM_CONTEXT_LOCAL; + } + else if (theMetric->source && strlen(theMetric->source) > 0) { + if (theMetric->isarch == 1) { + type = PM_CONTEXT_ARCHIVE; + group = archiveGroup; + } + } + + currentList = this; + source = theMetric->source; + sts = group->use(type, source); + if (sts >= 0) { + sts = pmTraversePMNS(theMetric->metric, dometric); + if (sts >= 0 && doMetricFlag == false) + sts = -1; + else if (sts < 0) + pmprintf("%s: Error: %s%c%s: %s\n", + pmProgname, + group->context()->source().sourceAscii(), + type == PM_CONTEXT_ARCHIVE ? '/' : ':', + theMetric->metric, + pmErrStr(sts)); + } + } + + free(theMetric); + + return sts; +} diff --git a/src/pmview/metriclist.h b/src/pmview/metriclist.h new file mode 100644 index 0000000..8ef1eee --- /dev/null +++ b/src/pmview/metriclist.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _METRICLIST_H_ +#define _METRICLIST_H_ + +#include <sys/types.h> +#include <Inventor/SbColor.h> +#include "qmc_metric.h" +#include "qmc_source.h" + +typedef QList<QmcMetric *> MetricsList; +typedef QList<SbColor *> SbColorList; + +class MetricList +{ +public: + + enum AlignColor { noColors, perMetric, perValue }; + +private: + + MetricsList _metrics; + SbColorList _colors; + int _values; + +public: + + ~MetricList(); + + MetricList(); + + int numMetrics() const + { return _metrics.size(); } + int numValues() const + { return _values; } + int numColors() const + { return _colors.size(); } + + const QmcMetric &metric(int i) const + { return *(_metrics[i]); } + QmcMetric &metric(int i) + { return *(_metrics[i]); } + + const SbColor &color(int i) const + { return *(_colors[i]); } + SbColor &color(int i) + { return *(_colors[i]); } + void color(int i, QString &str) const; + + int add(char const* metric, double scale); + int add(char const* metric, double scale, int history); + int add(pmMetricSpec *metric, double scale); + int add(pmMetricSpec *metric, double scale, int history); + int traverse(const char *metric); + + void add(SbColor const& color); + void add(int packedcol); + + void resolveColors(AlignColor align = perMetric); + + static void toString(const SbColor &color, QString &str); + + friend QTextStream& operator<<(QTextStream&, MetricList const &); + +private: + + MetricList(const MetricList &); + const MetricList &operator=(const MetricList &); + // Never defined +}; + +#endif /* _METRICLIST_H_ */ diff --git a/src/pmview/modlist.cpp b/src/pmview/modlist.cpp new file mode 100644 index 0000000..05a9c3d --- /dev/null +++ b/src/pmview/modlist.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <QtCore/QSettings> +#include <Inventor/SoPath.h> +#include <Inventor/SoPickedPoint.h> +#include <Inventor/events/SoLocation2Event.h> +#include <Inventor/nodes/SoEventCallback.h> +#include <Inventor/nodes/SoSelection.h> +#include <Inventor/nodes/SoIndexedFaceSet.h> +#include <Inventor/actions/SoBoxHighlightRenderAction.h> +#include <Inventor/Qt/viewers/SoQtExaminerViewer.h> +#include "modlist.h" + +#include <iostream> +using namespace std; + +const QString ModList::theBogusId = "TOP"; +const char ModList::theModListId = 'i'; + +ModList *theModList; +SoNodeList elementalNodeList; + +ModList::~ModList() +{ +} + +ModList::ModList(SoQtViewer *viewer, + SelCallBack selCB, + SelInvCallBack selInvCB, + SelInvCallBack deselInvCB) +: _viewer(viewer), + _selCB(selCB), + _selInvCB(selInvCB), + _deselInvCB(deselInvCB), + _root(0), + _selection(0), + _motion(0), + _rendAct(0), + _list(), + _selList(), + _current(1), + _numSel(0), + _oneSel(0), + _allFlag(false), + _allId(0) +{ + QSettings modSettings; + modSettings.beginGroup(pmProgname); + QString sval = modSettings.value("saturation", QString("")).toString(); + modSettings.endGroup(); + + bool ok; + double err = sval.toFloat(&ok); + if (!ok || err > 0.0) + theNormError = err; + + _root = new SoSeparator; + _root->ref(); +} + +void +ModList::setRoot(SoSeparator *root) +{ + _selection = new SoSelection; + _root->addChild(_selection); + + _selection->policy = SoSelection::SHIFT; + _selection->addSelectionCallback(&ModList::selCB, this); + _selection->addDeselectionCallback(&ModList::deselectCB, this); + + _motion = new SoEventCallback; + _motion->addEventCallback(SoLocation2Event::getClassTypeId(), + &ModList::motionCB, this); + _selection->addChild(_motion); + + root->setName((SbName)(const char *)theBogusId.toAscii()); + _selection->addChild(root); + + SoBoxHighlightRenderAction *rendAct = new SoBoxHighlightRenderAction; + _viewer->setGLRenderAction(rendAct); + _viewer->redrawOnSelectionChange(_selection); +} + +const char * +ModList::add(Modulate *obj) +{ + static char buf[32]; + int len = _list.size(); + int zero = 0; + + _list.append(obj); + _selList.append(zero); + + if (_current >= len) + _current = _list.size(); + + sprintf(buf, "%c%d", theModListId, _list.size() - 1); + return buf; +} + +void +ModList::refresh(bool fetchFlag) +{ + for (int i = 0; i < _list.size(); i++) + _list[i]->refresh(fetchFlag); + for (int n=elementalNodeList.getLength()-1; n >= 0; n--) { + elementalNodeList[n]->doAction(_viewer->getGLRenderAction()); + } +} + +void +ModList::dumpSelections(QTextStream &os) const +{ + int i; + int count = 0; + + os << _numSel << " selections (SoSelections.numSelections = " + << _selection->getNumSelected() << "), allFlag = " + << (_allFlag == true ? "true" : "false") << endl; + for (i = 0; i < _selList.size(); i++) + if (_selList[i] > 0) { + count += _selList[i]; + os << '[' << i << "]: "; + if (_numSel == 1 && _oneSel == i) + os << '*'; + os << *(_list[i]) << endl; + } + + assert(count == _numSel); +} + +QTextStream & +operator<<(QTextStream &os, const ModList &rhs) +{ + int i; + + for (i = 0; i < rhs._list.size(); i++) + os << '[' << i << "]: " << rhs[i] << endl; + return os; +} + +int +ModList::findToken(const SoPath *path) +{ + SoNode *node = NULL; + char *str = NULL; + int id = -1; + int i; + char c; + + for (i = path->getLength() - 1; i >= 0; --i) { + node = path->getNode(i); + str = (char *)(node->getName().getString()); + if (strlen(str) && str[0] == theModListId) { + sscanf(str, "%c%d", &c, &id); + break; + } + } + + return id; +} + +void +ModList::selCB(void *ptrToThis, SoPath *path) +{ + ModList *me = (ModList *)ptrToThis; + Modulate *obj; + int oldCount; + int id; + + if (!me->_allFlag) + id = ModList::findToken(path); + else + id = me->_allId; + + if (id < 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::selCB: Nothing selected" << endl; +#endif + + return; + /*NOTREACHED*/ + } + else if (!me->_allFlag) { + + obj = me->_list[id]; + oldCount = me->_selList[id]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::selCB: Before Selected [" << id << "] = " + << *obj << endl + << "oldCount = " << oldCount << ", _numSel = " + << me->_numSel << ", _allFlag = false" << endl; +#endif + + me->_selList[id] = obj->select(path); + + me->_numSel += me->_selList[id] - oldCount; + if (me->_numSel == 1) + me->_oneSel = id; + } + + if (!me->_allFlag) + (*(me->_selCB))(me, true); + + if (me->_selInvCB != NULL) + (*(me->_selInvCB))(me, path); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + cerr << "ModList::selCB: After Selected [" << id << "] " << endl + << "oldCount = " << oldCount << ", _numSel = " + << me->_numSel << ", _allFlag = " + << (me->_allFlag == true ? "true" : "false") << ", _allId = " + << me->_allId << endl; + cerr << "ModList::selCB: selection state:" << endl; + me->dumpSelections(cerr); + } +#endif +} + +void +ModList::deselectCB(void *ptrToThis, SoPath *path) +{ + ModList *me = (ModList *)ptrToThis; + Modulate *obj; + int oldCount; + int i; + int id; + + id = findToken(path); + + if (id < 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::deselectCB: Nothing deselected" << endl; +#endif + + return; + /*NOTREACHED*/ + } + + obj = me->_list[id]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::deselectCB: Deselected [" << id << "] = " + << *obj << endl; +#endif + + oldCount = me->_selList[id]; + me->_selList[id] = obj->remove(path); + me->_numSel -= oldCount - me->_selList[id]; + if (me->_numSel == 1) { + for (i = 0; i < me->_selList.size(); i++) + if (me->_selList[i] == 1) + me->_oneSel = i; + } + + if (me->_numSel == 0) + me->_current = me->_list.size(); + + if (me->_numSel < 2) + (*(me->_selCB))(me, true); + + if (me->_deselInvCB != NULL) + (*(me->_deselInvCB))(me, path); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + cerr << "ModList::deselectCB: selection state:" << endl; + me->dumpSelections(cerr); + } +#endif +} + +void +ModList::motionCB(void *ptrToThis, SoEventCallback *theEvent) +{ + ModList *me = (ModList *)ptrToThis; + const SoPickedPoint *pick = NULL; + SoPath *path = NULL; + int id = -1; + + // If one item is selected, return as we aren't interested + if (me->_numSel == 1) + return; + + pick = theEvent->getPickedPoint(); + if (pick != NULL) { + path = pick->getPath(); + if (path != NULL) + id = ModList::findToken(path); + } + + // Nothing selected that we are interested in + if (id < 0) { + // Deselect anything selected + if (me->_current < me->size()) { + (*me)[me->_current].removeInfo(path); + me->_current = me->size(); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::motionCB: remove object " << id << endl; +#endif + } + } + else if (me->_current != id) { + if (me->_current < me->size()) + (*me)[me->_current].removeInfo(path); + me->_current = id; + (*me)[me->_current].selectInfo(path); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::motionCB: new object " << id << endl; +#endif + } + else { + (*me)[me->_current].selectInfo(path); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::motionCB: same object " << id << endl; +#endif + } + + // Note: the call to _selCB below used to only be done if the guard + // if (old != me->_current) + // is true. But this does not work for stacked bars because + // the object is the same even though the mouse has moved over + // a different block in the same stack. Hence the metric info + // text window was not being updated for the mouse motion CB. + // Since the render method for the metricLabel only updates the + // text widget if the text has actually changed, it seems to me + // that it is safe to call the selCB unconditionally and there + // wont be any "flicker" problems. + // -- markgw 15 oct 1997 + // + (*(me->_selCB))(me, false); +} + +void +ModList::infoText(QString &str) const +{ + if (_current >= _list.size() && _numSel != 1) + str = ""; + else if (_numSel == 1) + _list[_oneSel]->infoText(str, true); + else + _list[_current]->infoText(str, false); +} + +void +ModList::launch(Launch &launch, bool all) const +{ + int i; + + if (all == false && _numSel > 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::launch: launching for " << _numSel + << " objects" << endl; +#endif + + for (i = 0; i < _selList.size(); i++) { + if (_selList[i] > 0) + _list[i]->launch(launch, false); + } + } + else { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::launch: launching for all objects" << endl; +#endif + + for (i = 0; i < _list.size(); i++) + _list[i]->launch(launch, true); + } +} + +void +ModList::record(Record &rec) const +{ + int i; + + for (i = 0; i < _list.size(); i++) + _list[i]->record(rec); +} + +bool +ModList::selections() const +{ + if (_numSel) + return true; + + return false; +} + +const SoPath * +ModList::oneSelPath() const +{ + return (_numSel == 1 ? _selection->getPath(0) : NULL); +} + +void +ModList::deselectPath(SoPath *path) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::deselectPath:" << endl; +#endif + + _selection->deselect(path); + deselectCB(this, path); +} + +void +ModList::selectAllId(SoNode *node, int count) +{ + SoPath *path = new SoPath(node); + + _allId = findToken(path); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "ModList::selectAllId: Select All on " << _allId << endl; +#endif + + if (_allId > 0) { + _numSel += count - _selList[_allId]; + _selList[_allId] = count; + if (_numSel == 1) + _oneSel = _allId; + } + + path->unref(); +} + +void +ModList::selectSingle(SoNode *node) +{ + _selection->select(node); +} + +void +ModList::selectAllOff() +{ + _allFlag = false; + _allId = _list.size(); + (*_selCB)(this, true); +} diff --git a/src/pmview/modlist.h b/src/pmview/modlist.h new file mode 100644 index 0000000..72d2319 --- /dev/null +++ b/src/pmview/modlist.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _MODLIST_H_ +#define _MODLIST_H_ + +#include <Inventor/SoLists.h> +#include "modulate.h" + +class SoQtViewer; +class SoSelection; +class SoEventCallback; +class SoBoxHighlightRenderAction; +class SoPath; +class SoPickedPoint; +class SoSeparator; +class ModList; +class Launch; +class Record; + +typedef void (*SelCallBack)(ModList *, bool); +typedef void (*SelInvCallBack)(ModList *, SoPath *); + +typedef QList<Modulate *> ModulateList; +extern SoNodeList elementalNodeList; + +class ModList +{ +private: + + static const QString theBogusId; + static const char theModListId; + + SoQtViewer *_viewer; + SelCallBack _selCB; + SelInvCallBack _selInvCB; + SelInvCallBack _deselInvCB; + SoSeparator *_root; + SoSelection *_selection; + SoEventCallback *_motion; + SoBoxHighlightRenderAction *_rendAct; + + ModulateList _list; + QList<int> _selList; + int _current; + int _numSel; + int _oneSel; + + bool _allFlag; + int _allId; + +public: + + ~ModList(); + + ModList(SoQtViewer *viewer, SelCallBack selCB, + SelInvCallBack selInvCB = NULL, SelInvCallBack deselInvCB = NULL); + + int size() const + { return _list.size(); } + int numSelected() const + { return _numSel; } + + SoSeparator *root() + { return _root; } + void setRoot(SoSeparator *root); + + SoSelection *selector() const + { return _selection; } + + const char *add(Modulate *obj); + + const Modulate &operator[](int i) const + { return *(_list[i]); } + Modulate &operator[](int i) + { return *(_list[i]); } + + const Modulate ¤t() const + { return *(_list[_current]); } + Modulate ¤t() + { return *(_list[_current]); } + + void refresh(bool fetchFlag); + + void infoText(QString &str) const; + + void launch(Launch &launch, bool all = false) const; + void record(Record &rec) const; + + void dumpSelections(QTextStream &os) const; + bool selections() const; + + void selectAllOn() + { _allFlag = true; _allId = _list.size(); } + void selectAllId(SoNode *node, int count); + void selectSingle(SoNode *node); + void selectAllOff(); + + friend QTextStream &operator<<(QTextStream &os, const ModList &rhs); + + static void deselectCB(void *me, SoPath *path); + + // Sprouting support + + // Get path to single selection + const SoPath *oneSelPath() const; + + void deselectPath(SoPath *path); + +private: + + static void selCB(void *me, SoPath *path); + static void motionCB(void *me, SoEventCallback *event); + static int findToken(const SoPath *path); + + ModList(const ModList &); + const ModList &operator=(const ModList &); + // Never defined +}; + +extern ModList *theModList; + +#endif /* _MODLIST_H_ */ diff --git a/src/pmview/modobj.h b/src/pmview/modobj.h new file mode 100644 index 0000000..0d65f4b --- /dev/null +++ b/src/pmview/modobj.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _MODOBJ_H_ +#define _MODOBJ_H_ + +#include "baseobj.h" + +class ModObj : public BaseObj +{ +public: + ModObj(bool onFlag, + const DefaultObj &defaults, + int x, int y, + int cols = 1, int rows = 1, + BaseObj::Alignment align = BaseObj::center) + : BaseObj (onFlag, defaults, x, y, cols, rows, align) + , _history(0) + , _colors() + , _metrics () + { _objtype |= MODOBJ; } + + void setColorList(const char *list) { _colors = list; } + void addMetric(const char * m, double s) { _metrics.add(m, s, _history); } + void setHistory(int history) { _history = history; } + +protected: + int _history; + QString _colors; + MetricList _metrics; +}; +#endif diff --git a/src/pmview/modulate.cpp b/src/pmview/modulate.cpp new file mode 100644 index 0000000..8e3fb98 --- /dev/null +++ b/src/pmview/modulate.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoSelection.h> +#include "modulate.h" +#include "modlist.h" + +#include <iostream> +using namespace std; + +double theNormError = 1.05; + +const QString Modulate::theErrorText = "Metric Unavailable"; +const QString Modulate::theStartText = "Metric has not been fetched from source"; +const float Modulate::theDefErrorColor[] = {0.2, 0.2, 0.2}; +const float Modulate::theDefSaturatedColor[] = {1.0, 1.0, 1.0}; +const double Modulate::theMinScale = 0.01; + +Modulate::~Modulate() +{ +} + +Modulate::Modulate(const char *metric, double scale, + MetricList::AlignColor align) +: _sts(0), _metrics(0), _root(0) +{ + _metrics = new MetricList(); + _sts = _metrics->add(metric, scale); + if (_sts >= 0) { + _metrics->resolveColors(align); + _saturatedColor.setValue(theDefSaturatedColor); + } + _errorColor.setValue(theDefErrorColor); +} + +Modulate::Modulate(const char *metric, double scale, + const SbColor &color, + MetricList::AlignColor align) +: _sts(0), _metrics(0), _root(0) +{ + _metrics = new MetricList(); + _sts = _metrics->add(metric, scale); + if (_sts >= 0) { + _metrics->add(color), + _metrics->resolveColors(align); + } + _saturatedColor.setValue(theDefSaturatedColor); + _errorColor.setValue(theDefErrorColor); +} + +Modulate::Modulate(MetricList *list) +: _sts(0), _metrics(list), _root(0) +{ + _saturatedColor.setValue(theDefSaturatedColor); + _errorColor.setValue(theDefErrorColor); +} + +const char * +Modulate::add() +{ + const char *str = theModList->add(this); + _root->setName((SbName)str); + return str; +} + +QTextStream & +operator<<(QTextStream & os, const Modulate &rhs) +{ + rhs.dump(os); + return os; +} + +void +Modulate::dumpState(QTextStream &os, Modulate::State state) const +{ + switch(state) { + case Modulate::start: + os << "Start"; + break; + case Modulate::error: + os << "Error"; + break; + case Modulate::saturated: + os << "Saturated"; + break; + case Modulate::normal: + os << "Normal"; + break; + default: + os << "Unknown"; + break; + } +} + +void +Modulate::record(Record &rec) const +{ +#if 1 // TODO + (void)rec; +#else // TODO + int i; + + if (_metrics != NULL) + for (i = 0; i < _metrics->numMetrics(); i++) { + const QmcMetric &metric = _metrics->metric(i); + rec.add(metric.context()->source().sourceAscii(), + (const char *)metric.spec(false, true).toAscii()); + } +#endif +} + +void +Modulate::selectAll() +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "Modulate::selectAll: selectAll for " << *this << endl; +#endif + + theModList->selectAllId(_root, 1); + theModList->selectSingle(_root); +} diff --git a/src/pmview/modulate.h b/src/pmview/modulate.h new file mode 100644 index 0000000..b8e6b7d --- /dev/null +++ b/src/pmview/modulate.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _MODULATE_H_ +#define _MODULATE_H_ + +#include <Inventor/SbString.h> +#include "metriclist.h" + +class SoSeparator; +class SoPath; +class Launch; +class Record; + +extern double theNormError; +extern float theScale; + +class Modulate +{ +public: + + enum State { start, error, saturated, normal }; + +protected: + + static const QString theErrorText; + static const QString theStartText; + static const float theDefErrorColor[]; + static const float theDefSaturatedColor[]; + static const double theMinScale; + + int _sts; + MetricList *_metrics; + SoSeparator *_root; + SbColor _errorColor; + SbColor _saturatedColor; + +public: + + virtual ~Modulate(); + + Modulate(const char *metric, double scale, + MetricList::AlignColor align = MetricList::perMetric); + + Modulate(const char *metric, double scale, const SbColor &color, + MetricList::AlignColor align = MetricList::perMetric); + + Modulate(MetricList *list); + + int status() const + { return _sts; } + const SoSeparator *root() const + { return _root; } + SoSeparator *root() + { return _root; } + + int numValues() const + { return _metrics->numValues(); } + + const char *add(); + + void setErrorColor(const SbColor &color) + { _errorColor.setValue(color.getValue()); } + void setSaturatedColor(const SbColor &color) + { _saturatedColor.setValue(color.getValue()); } + + virtual void refresh(bool fetchFlag) = 0; + + // Return the number of objects still selected + virtual void selectAll(); + virtual int select(SoPath *) + { return 0; } + virtual int remove(SoPath *) + { return 0; } + + // Should expect selectInfo calls to different paths without + // previous removeInfo calls + virtual void selectInfo(SoPath *) + {} + virtual void removeInfo(SoPath *) + {} + + virtual void infoText(QString &str, bool selected) const = 0; + + virtual void launch(Launch &launch, bool all) const = 0; + virtual void record(Record &rec) const; + + virtual void dump(QTextStream &) const + {} + void dumpState(QTextStream &os, State state) const; + + friend QTextStream &operator<<(QTextStream &os, const Modulate &rhs); + +protected: + + static void add(Modulate *obj); + +private: + + Modulate(); + Modulate(const Modulate &); + const Modulate &operator=(const Modulate &); + // Never defined +}; + +#endif /* _MODULATE_H_ */ diff --git a/src/pmview/pcpcolor.cpp b/src/pmview/pcpcolor.cpp new file mode 100644 index 0000000..85a313e --- /dev/null +++ b/src/pmview/pcpcolor.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/actions/SoCallbackAction.h> +#include <Inventor/actions/SoGLRenderAction.h> +#include <Inventor/bundles/SoMaterialBundle.h> +#include <Inventor/elements/SoEmissiveColorElement.h> + +#include "pcpcolor.h" +#include "scenegroup.h" +#include "main.h" + +SO_NODE_SOURCE(PCPColor); +// Initializes the PCPColor class. This is a one-time thing that is +// done after database initialization and before any instance of +// this class is constructed. +void +PCPColor::initClass() +{ + // Initialize type id variables. The arguments to the macro + // are: the name of the node class, the class this is derived + // from, and the name registered with the type of the parent + // class. + SO_NODE_INIT_CLASS(PCPColor, SoNode, "Node"); +} +// Constructor +PCPColor::PCPColor() +{ + // Do standard constructor tasks + SO_NODE_CONSTRUCTOR(PCPColor); + + SO_NODE_ADD_FIELD(maxValue, (1.0)); + SO_NODE_ADD_FIELD(color, (1.0, 1.0, 1.0)); + SO_NODE_ADD_FIELD(metric, ("")); + + // SbString s = metric.getValue(); + SbString *s = new SbString("kernel.all.cpu.user"); + + double scale = (double)maxValue.getValue(); + theMetric = new QmcMetric(activeGroup, s->getString(), scale); + elementalNodeList.append(this); +} +// Destructor +PCPColor::~PCPColor() +{ +} +// Implements GL render action. +void +PCPColor::GLRender(SoGLRenderAction *action) +{ + // Set the elements in the state correctly. Note that we + // prefix the call to doAction() with the class name. This + // avoids problems if someone derives a new class from the + // PCPColor node and inherits the GLRender() method; PCPColor's + // doAction() will still be called in that case. + + PCPColor::doAction(action); + + // For efficiency, Inventor nodes make sure that the first + // defined material is always in GL, so shapes do not have to + // send the first material each time. (This keeps caches from + // being dependent on material values in many cases.) The + // SoMaterialBundle class allows us to do this easily. + SoMaterialBundle mb(action); + mb.forceSend(0); +} +// Implements callback action. +void +PCPColor::callback(SoCallbackAction *action) +{ + // Set the elements in the state correctly. + PCPColor::doAction(action); +} + +// Typical action implementation - it sets the correct element +// in the action's traversal state. We assume that the element +// has been enabled. +void +PCPColor::doAction(SoAction *action) +{ + theMetric->update(); + float f = theMetric->realValue(0); + f /= maxValue.getValue() + 0.001; + emissiveColor = color.getValue() * f; + if (action->getState()) + SoEmissiveColorElement::set(action->getState(), this, 1, &emissiveColor); + + touch(); +} diff --git a/src/pmview/pcpcolor.h b/src/pmview/pcpcolor.h new file mode 100644 index 0000000..85d47df --- /dev/null +++ b/src/pmview/pcpcolor.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _PCPCOLOR_H_ +#define _PCPCOLOR_H_ + +#include <Inventor/SbColor.h> +#include <Inventor/fields/SoSFString.h> +#include <Inventor/fields/SoSFColor.h> +#include <Inventor/fields/SoSFFloat.h> +#include <Inventor/nodes/SoSubNode.h> + +#include "main.h" +#include "modlist.h" + +class PCPColor : public SoNode { + SO_NODE_HEADER(PCPColor); + public: + // Fields: + SoSFString metric; // PCP metric spec + SoSFFloat maxValue; + SoSFColor color; // Color of glow + + // Initializes this class for use in scene graphs. This + // should be called after database initialization and before + // any instance of this node is constructed. + static void initClass(); + // Constructor + PCPColor(); + protected: + QmcMetric *theMetric; + + // These implement supported actions. The only actions that + // deal with materials are the callback and GL render + // actions. We will inherit all other action methods from + // SoNode. + virtual void GLRender(SoGLRenderAction *action); + virtual void callback(SoCallbackAction *action); + // This implements generic traversal of PCPColor node, used in + // both of the above methods. + virtual void doAction(SoAction *action); + + private: + // Destructor. Private to keep people from trying to delete + // nodes, rather than using the reference count mechanism. + virtual ~PCPColor(); + SbColor emissiveColor; +}; + +#endif /* _PCPCOLOR_H_ */ diff --git a/src/pmview/pipeobj.cpp b/src/pmview/pipeobj.cpp new file mode 100644 index 0000000..e6e8886 --- /dev/null +++ b/src/pmview/pipeobj.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoRotationXYZ.h> +#include <Inventor/nodes/SoCylinder.h> +#include <Inventor/nodes/SoCube.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> + +#include "stackmod.h" +#include "togglemod.h" +#include "pipeobj.h" +#include "defaultobj.h" +#include "colorlist.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Constructor for Pipe. + * Note that similar to Link, pipes are always centered + * relative to the grid. The aligment is used to decide + * which end of pipe is decorated and in what direction + * pipe is oriented. +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +PipeObj::PipeObj (const DefaultObj & defs, + int c, int r, + int colSpan, int rowSpan, + BaseObj::Alignment align) + : ModObj (false, defs, c, r, colSpan, rowSpan, center) + , _align(align) + , _tag ("\n") +{ + _objtype |= PIPEOBJ; + + for ( int i=0; i < 3; i++) { + _color[i] = defs.baseColor(i); + } + + _cylTrans = 0; + _origt = 0; + _cyl = 0; + _stackHeight = defs.pipeLength(); + cellHeight = defs.baseHeight(); + + if ( _align == north || _align == south ) { + cellWidth = defs.baseHeight(); + cellDepth = (defs.pipeLength() < cellHeight) ? + cellHeight : defs.pipeLength(); + } else { + cellWidth = (defs.pipeLength() < cellHeight) ? + cellHeight : defs.pipeLength(); + cellDepth = defs.baseHeight(); + } + + _width = colSpan * cellWidth; + _depth = rowSpan * cellDepth; + +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void +PipeObj::finishedAdd () +{ + BaseObj::addBase (_root); + + int h; + SoSeparator * sp = new SoSeparator; + _root->addChild (sp); + + SoTranslation * st = new SoTranslation; + st->translation.setValue (_width/2.0, cellHeight/2.0, _depth/2.0); + sp->addChild (st); + + SoRotationXYZ * r = new SoRotationXYZ; + if ( _align == north || _align == south ) { // Vertical Pipe + h = _depth; + + r->axis = SoRotationXYZ::X; + r->angle = M_PI/((_align == north) ? 2 : -2); + } else { // Horizontal pipe + h = _width; + + r->axis = SoRotationXYZ::Z; + r->angle = M_PI/((_align == east) ? 2 : -2); + } + sp->addChild (r); + + SoSeparator * sceneSp = new SoSeparator; + sp->addChild (sceneSp); + + _origt = new SoTranslation; + _origt->translation.setValue (0, -h/2.0, 0); + sceneSp->addChild (_origt); + + if (_metrics.numMetrics() ) { + SoSeparator * stacksp = new SoSeparator; + const ColorSpec * colSpec; + SoScale *scale = new SoScale(); + scale->scaleFactor.setValue(cellHeight*0.8, _stackHeight, + cellHeight*0.8); + stacksp->addChild(scale); + + if ((colSpec = theColorLists.list((const char *)_colors.toAscii()))) { + if (colSpec->_scale) + pmprintf( + "%s: Warning: Color scale cannot be applied to pipe\n", + pmProgname); + else { + for (int i = 0; i < colSpec->_list.size(); i++) + _metrics.add(*(colSpec->_list)[i]); + } + } else { + pmprintf("%s: Warning: No colours specified for pipe" + "defaulting to blue.\n", pmProgname); + } + + _metrics.resolveColors(MetricList::perValue); + + StackMod * _stack = new StackMod(&_metrics, ViewObj::object (cylinder), + StackMod::fixed); + _stack->setFillColor(_color); + _stack->setFillText((const char *)_tag.toAscii()); + + stacksp->addChild(_stack->root()); + sceneSp->addChild(stacksp); + + BaseObj::add(_stack); + ViewObj::theNumModObjects++; + } else { + pmprintf("%s: Error: no metrics for pipe\n", pmProgname); + } + + SoBaseColor * color = new SoBaseColor; + color->rgb.setValue (_color); + sceneSp->addChild (color); + + _cylTrans = new SoTranslation; + _cylTrans->translation.setValue (0, (h+_stackHeight)/2.0, 0); + sceneSp->addChild (_cylTrans); + + + _cyl = new SoCylinder; + _cyl->radius.setValue (cellHeight*0.4); + _cyl->height.setValue (h - _stackHeight); + + // In theory, something like "StaticMod", i.e no metrics, no + // nothing should be provided, but togglemod seems to be + // working Ok, so.... + ToggleMod * m = new ToggleMod (_cyl, (const char *)_tag.toAscii()); + sceneSp->addChild (m->root()); +} + +void +PipeObj::setTran (float x, float z, int w, int d) +{ + if ( _cyl ) { + float h = (_align == north || _align == south) ? d : w; + + _cylTrans->translation.setValue (0, (h + _stackHeight)/2.0, 0); + _origt->translation.setValue (0, -h/2.0, 0); + _cyl->height.setValue (h - _stackHeight); + } + + ModObj::setTran (x, z, w, d); +} + +void +PipeObj::setTag (const char * s) +{ + _tag = s; + if ( strchr (s, '\n' ) == NULL ) { + _tag.append ("\n"); + } +} diff --git a/src/pmview/pipeobj.h b/src/pmview/pipeobj.h new file mode 100644 index 0000000..1630813 --- /dev/null +++ b/src/pmview/pipeobj.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _PIPEOBJ_H_ +#define _PIPEOBJ_H_ + +#include "modobj.h" + +class SoCylinder; +class SoTranslation; + +class PipeObj : public ModObj { +public: + virtual ~PipeObj () {} + PipeObj (const DefaultObj &, int, int, int, int, Alignment); + + int width () const { return _width; } + int depth () const { return _depth; } + + const char * name() const { return "Pipe"; } + + void finishedAdd (); + void setTran (float, float, int, int); + void setTag (const char *); + +private: + PipeObj (); + PipeObj (PipeObj const &); + PipeObj const& operator=(PipeObj const &); + + int _width, _depth; + int cellWidth, cellDepth, cellHeight; + Alignment _align; + float _stackHeight; + QString _tag; + + SoCylinder * _cyl; + SoTranslation * _cylTrans, * _origt; + + float _color[3]; +}; + +#endif /* _PIPEOBJ_H_ */ diff --git a/src/pmview/pmview.cpp b/src/pmview/pmview.cpp new file mode 100644 index 0000000..2d4d1ff --- /dev/null +++ b/src/pmview/pmview.cpp @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2007-2009, Aconex. All Rights Reserved. + * Copyright (c) 2006, Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <QtCore/QUrl> +#include <QtCore/QTimer> +#include <QtCore/QLibraryInfo> +#include <QtGui/QDesktopServices> +#include <QtGui/QApplication> +#include <QtGui/QPrintDialog> +#include <QtGui/QMessageBox> +#include <QtGui/QWhatsThis> + +#include <Inventor/nodes/SoPerspectiveCamera.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTransform.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoCube.h> + +#include <iostream> +using namespace std; + +#include "barobj.h" +#include "gridobj.h" +#include "stackobj.h" +#include "labelobj.h" +#include "modlist.h" +#include "defaultobj.h" +#include "pcpcolor.h" + +#include "main.h" +#include "pmview.h" +#include "scenegroup.h" +#include "qed_console.h" +#include "qed_statusbar.h" +#include "qed_timecontrol.h" +#include "qed_recorddialog.h" + +QString theConfigName; +QString theAltConfigName; +FILE *theConfigFile; +FILE *theAltConfig; +float theGlobalScale = 1.2; +char **frontend_argv; +int frontend_argc; + +PmView::PmView() : QMainWindow(NULL) +{ + my.dialogsSetup = false; + + setIconSize(QSize(22, 22)); + setupUi(this); + + SoQt::init(widget); + my.viewer = new SoQtExaminerViewer(widget); + + my.statusBar = new QedStatusBar; + setStatusBar(my.statusBar); + + toolBar->setAllowedAreas(Qt::RightToolBarArea | Qt::TopToolBarArea); + connect(toolBar, SIGNAL(orientationChanged(Qt::Orientation)), + this, SLOT(updateToolbarOrientation(Qt::Orientation))); + updateToolbarLocation(); + setupEnabledActionsList(); + if (!globalSettings.initialToolbar) + toolBar->hide(); + + my.liveHidden = true; + my.archiveHidden = true; + timeControlAction->setChecked(false); + my.menubarHidden = false; + my.toolbarHidden = !globalSettings.initialToolbar; + toolbarAction->setChecked(globalSettings.initialToolbar); + my.consoleHidden = true; + if (!pmDebug) + consoleAction->setVisible(false); + consoleAction->setChecked(false); + + // Build Scene Graph + my.root = new SoSeparator; + my.root->ref(); + + SoPerspectiveCamera *camera = new SoPerspectiveCamera; + camera->orientation.setValue(SbVec3f(1, 0, 0), -M_PI/6.0); + my.root->addChild(camera); + + my.drawStyle = new SoDrawStyle; + my.drawStyle->style.setValue(SoDrawStyle::FILLED); + my.root->addChild(my.drawStyle); + +#if 0 + // TODO is this needed? + if (outfile) + QTimer::singleShot(0, this, SLOT(exportFile())); + else +#endif + QTimer::singleShot(PmView::defaultTimeout(), this, SLOT(timeout())); + +} + +void PmView::languageChange() +{ + retranslateUi(this); +} + +void PmView::init(void) +{ + my.statusBar->init(); +} + +void +PmView::selectionCB(ModList *, bool redraw) +{ + RenderOptions options = PmView::metricLabel; + + if (redraw) + options = (RenderOptions)(options | PmView::inventor); + pmview->render(options, 0); +} + +bool PmView::view(bool showAxis, + float xAxis, float yAxis, float zAxis, float angle, float scale) +{ + if (theModList->size() == 0) { + warningMsg(_POS_, "No modulated objects in scene"); + } + + // Setup remainder of the scene graph + my.root->addChild(theModList->root()); + + viewer()->setSceneGraph(my.root); + viewer()->setAutoRedraw(true); + viewer()->setTitle(pmProgname); + if (showAxis) + viewer()->setFeedbackVisibility(true); + + SbBool smooth = TRUE; + int passes = 1; + char *sval = NULL; + +#if 0 // TODO: QSettings API + sval = VkGetResource("antiAliasSmooth", XmRString); + if (sval && strcmp(sval, "default") != 0 && strcasecmp(sval, "false") == 0) + smooth = FALSE; + sval = VkGetResource("antiAliasPasses", XmRString); + if (sval != NULL && strcmp(sval, "default")) + passes = atoi(sval); +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "PmView::view: antialiasing set to smooth = " + << (smooth == TRUE ? "true" : "false") + << ", passes = " << passes << endl; +#endif + + if (passes > 1) + viewer()->setAntialiasing(smooth, atoi(sval)); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "PmView::view: displaying window" << endl; +#endif + + viewer()->viewAll(); + + if (angle != 0.0 || scale != 0.0) { + SoTransform *tran = new SoTransform(); + if (angle != 0.0) + tran->rotation.setValue(SbVec3f(xAxis, yAxis, zAxis), angle); + if (scale != 0.0) + tran->scaleFactor.setValue(scale, scale, scale); + theModList->root()->insertChild(tran, 0); + } + + PmView::render((RenderOptions)(PmView::inventor | PmView::metricLabel), 0); + viewer()->saveHomePosition(); + + return true; +} + +void PmView::render(RenderOptions options, time_t theTime) +{ + viewer()->setAutoRedraw(false); + + if (options & PmView::metrics) + theModList->refresh(true); + + if (options & PmView::inventor) + viewer()->render(); + + if (options & PmView::metricLabel) { + theModList->infoText(my.text); + if (my.text != my.prevText) { + my.prevText = my.text; + if (my.text.length() == 0) + // TODO: clear label string + ; + else { + // TODO: set label string to my.text +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "PmView::render: metricLabel text \"" << + my.text() << "\"" << endl; +#endif + } + } + } + + if (options & PmView::timeLabel) + setDateLabel(theTime, QString::null); // TODO + + viewer()->setAutoRedraw(true); +} + +QMenu *PmView::createPopupMenu(void) +{ + QMenu *menu = QMainWindow::createPopupMenu(); + menu->addAction(menubarAction); + return menu; +} + +void PmView::quit() +{ + // End any processes we may have started and close any open dialogs + if (pmtime) + pmtime->quit(); +} + +void PmView::closeEvent(QCloseEvent *) +{ + quit(); +} + +void PmView::enableUi(void) +{ +#if 0 + recordStartAction->setEnabled(haveGadgets && haveLiveHosts && !haveLoggers); + recordQueryAction->setEnabled(haveLoggers); + recordStopAction->setEnabled(haveLoggers); + recordDetachAction->setEnabled(haveLoggers); +#endif +} + +void PmView::updateToolbarLocation() +{ +#if QT_VERSION >= 0x040300 + setUnifiedTitleAndToolBarOnMac(globalSettings.nativeToolbar); +#endif + if (globalSettings.toolbarLocation) + addToolBar(Qt::RightToolBarArea, toolBar); + else + addToolBar(Qt::TopToolBarArea, toolBar); +} + +void PmView::updateToolbarOrientation(Qt::Orientation orientation) +{ + (void)orientation; + // TODO +} + +void PmView::setButtonState(QedTimeButton::State state) +{ + my.statusBar->timeButton()->setButtonState(state); +} + +void PmView::step(bool live, QmcTime::Packet *packet) +{ + if (live) + liveGroup->step(packet); + else + archiveGroup->step(packet); +} + +void PmView::VCRMode(bool live, QmcTime::Packet *packet, bool drag) +{ + if (live) + liveGroup->VCRMode(packet, drag); + else + archiveGroup->VCRMode(packet, drag); +} + +void PmView::timeZone(bool live, QmcTime::Packet *packet, char *tzdata) +{ + if (live) + liveGroup->setTimezone(packet, tzdata); + else + archiveGroup->setTimezone(packet, tzdata); +} + +void PmView::filePrint() +{ + QMessageBox::information(this, pmProgname, "Print, print, print... whirrr"); +} + +void PmView::fileQuit() +{ + QApplication::exit(0); +} + +void PmView::helpManual() +{ + bool ok; + QString documents("file://"); + QString separator = QString(__pmPathSeparator()); + documents.append(pmGetConfig("PCP_HTML_DIR")); + documents.append(separator).append("index.html"); + ok = QDesktopServices::openUrl(QUrl(documents, QUrl::TolerantMode)); + if (!ok) { + documents.prepend("Failed to open:\n"); + QMessageBox::warning(this, pmProgname, documents); + } +} + +void PmView::helpTutorial() +{ + bool ok; + QString documents("file://"); + QString separator = QString(__pmPathSeparator()); + documents.append(pmGetConfig("PCP_HTML_DIR")); + documents.append(separator).append("tutorial.html"); + ok = QDesktopServices::openUrl(QUrl(documents, QUrl::TolerantMode)); + if (!ok) { + documents.prepend("Failed to open:\n"); + QMessageBox::warning(this, pmProgname, documents); + } +} + +void PmView::helpAbout() +{ +#if 0 + AboutDialog about(this); + about.exec(); +#endif +} + +void PmView::helpSeeAlso() +{ +#if 0 + SeeAlsoDialog seealso(this); + seealso.exec(); +#endif +} + +void PmView::whatsThis() +{ + QWhatsThis::enterWhatsThisMode(); +} + +void PmView::optionsTimeControl() +{ + if (activeView()->isArchiveSource()) { + if (my.archiveHidden) + pmtime->showArchiveTimeControl(); + else + pmtime->hideArchiveTimeControl(); + my.archiveHidden = !my.archiveHidden; + timeControlAction->setChecked(!my.archiveHidden); + } + else { + if (my.liveHidden) + pmtime->showLiveTimeControl(); + else + pmtime->hideLiveTimeControl(); + my.liveHidden = !my.liveHidden; + timeControlAction->setChecked(!my.liveHidden); + } +} + +void PmView::optionsToolbar() +{ + if (my.toolbarHidden) + toolBar->show(); + else + toolBar->hide(); + my.toolbarHidden = !my.toolbarHidden; +} + +void PmView::optionsMenubar() +{ + if (my.menubarHidden) + MenuBar->show(); + else + MenuBar->hide(); + my.menubarHidden = !my.menubarHidden; +} + +void PmView::optionsConsole() +{ +#if 0 + if (pmDebug) { + if (my.consoleHidden) + console->show(); + else + console->hide(); + my.consoleHidden = !my.consoleHidden; + } +#endif +} + +void PmView::optionsNewPmchart() +{ + QProcess *buddy = new QProcess(this); + QStringList arguments; + QString port; + + port.setNum(pmtime->port()); + arguments << "-p" << port; + for (unsigned int i = 0; i < archiveGroup->numContexts(); i++) { + QmcSource source = archiveGroup->context(i)->source(); + arguments << "-a" << source.source(); + } + for (unsigned int i = 0; i < liveGroup->numContexts(); i++) { + QmcSource source = liveGroup->context(i)->source(); + arguments << "-h" << source.source(); + } + if (Lflag) + arguments << "-L"; + buddy->start("pmview", arguments); +} + +bool PmView::isViewRecording() +{ + return activeView()->isRecording(); +} + +bool PmView::isArchiveView() +{ + return activeView()->isArchiveSource(); +} + +void PmView::setDateLabel(time_t seconds, QString tz) +{ + char datestring[32]; + QString label; + + if (seconds) { + pmCtime(&seconds, datestring); + label = tr(datestring); + label.remove(10, 9); + label.replace(15, 1, " "); + label.append(tz); + } + else { + label = tr(""); + } + my.statusBar->setDateText(label); +} + +void PmView::setDateLabel(QString label) +{ + my.statusBar->setDateText(label); +} + +void PmView::setRecordState(bool record) +{ + liveGroup->newButtonState(liveGroup->pmtimeState(), + QmcTime::NormalMode, record); + setButtonState(liveGroup->buttonState()); + enableUi(); +} + +void PmView::recordStart() +{ + if (activeView()->startRecording()) + setRecordState(true); +} + +void PmView::recordStop() +{ + activeView()->stopRecording(); +} + +void PmView::recordQuery() +{ + activeView()->queryRecording(); +} + +void PmView::recordDetach() +{ + activeView()->detachLoggers(); +} + +QList<QAction*> PmView::toolbarActionsList() +{ + return my.toolbarActionsList; +} + +QList<QAction*> PmView::enabledActionsList() +{ + return my.enabledActionsList; +} + +void PmView::setupEnabledActionsList() +{ + // ToolbarActionsList is a list of all Actions available. + // The SeparatorsList contains Actions that are group "breaks", and + // which must be followed by a separator (if they are not the final + // action in the toolbar, of course). + // Finally the enabledActionsList lists the default enabled Actions. + + my.toolbarActionsList << filePrintAction; + addSeparatorAction(); // end exported formats + my.toolbarActionsList << recordStartAction << recordStopAction; + addSeparatorAction(); // end recording group + //my.toolbarActionsList << editSettingsAction; + //addSeparatorAction(); // end settings group + my.toolbarActionsList << timeControlAction; + addSeparatorAction(); // end other processes + my.toolbarActionsList << helpManualAction << helpWhatsThisAction; + + // needs to match pmview.ui + my.enabledActionsList << filePrintAction; + + if (globalSettings.toolbarActions.size() > 0) { + setEnabledActionsList(globalSettings.toolbarActions, false); + updateToolbarContents(); + } +} + +void PmView::addSeparatorAction() +{ + int index = my.toolbarActionsList.size() - 1; + my.separatorsList << my.toolbarActionsList.at(index); +} + +void PmView::updateToolbarContents() +{ + bool needSeparator = false; + + toolBar->clear(); + for (int i = 0; i < my.toolbarActionsList.size(); i++) { + QAction *action = my.toolbarActionsList.at(i); + if (my.enabledActionsList.contains(action)) { + toolBar->addAction(action); + if (needSeparator) { + toolBar->insertSeparator(action); + needSeparator = false; + } + } + if (my.separatorsList.contains(action)) + needSeparator = true; + } +} + +void PmView::setEnabledActionsList(QStringList tools, bool redisplay) +{ + my.enabledActionsList.clear(); + for (int i = 0; i < my.toolbarActionsList.size(); i++) { + QAction *action = my.toolbarActionsList.at(i); + if (tools.contains(action->iconText())) + my.enabledActionsList.append(action); + } + + if (redisplay) { + my.toolbarHidden = (my.enabledActionsList.size() == 0); + toolbarAction->setChecked(my.toolbarHidden); + if (my.toolbarHidden) + toolBar->hide(); + else + toolBar->show(); + } +} + +void View::init(SceneGroup *group, QMenu *menu, QString title) +{ + my.group = group; + QedViewControl::init(group, menu, title, globalSettings.loggerDelta); +} + +QStringList View::hostList(bool) +{ + // TODO + return QStringList(); +} + +QString View::pmloggerSyntax(bool) +{ +// TODO +#if 0 + View *view = pmview->activeView(); + QString configdata; + + if (selectedOnly) + configdata.append(pmview->activeGadget()->pmloggerSyntax()); + else + for (int c = 0; c < view->gadgetCount(); c++) + configdata.append(gadget(c)->pmloggerSyntax()); + return configdata; +#else + return NULL; +#endif +} + +bool View::saveConfig(QString filename, bool hostDynamic, + bool sizeDynamic, bool allViews, bool allCharts) +{ +// TODO +#if 0 + return SaveViewDialog::saveView(filename, + hostDynamic, sizeDynamic, allViews, allCharts); +#else + return false; +#endif +} + +bool View::stopRecording() +{ +// TODO +#if 0 + QString errmsg; + bool error = ViewControl::stopRecording(errmsg); + QStringList archiveList = ViewControl::archiveList(); + + for (int i = 0; i < archiveList.size(); i++) { + QString archive = archiveList.at(i); + int sts; + + console->post("View::stopRecording opening archive %s", + (const char *)archive.toAscii()); + if ((sts = archiveGroup->use(PM_CONTEXT_ARCHIVE, archive)) < 0) { + errmsg.append(QApplication::tr("Cannot open PCP archive: ")); + errmsg.append(archive); + errmsg.append("\n"); + errmsg.append(pmErrStr(sts)); + errmsg.append("\n"); + error = true; + } + else { + archiveGroup->updateBounds(); + QmcSource source = archiveGroup->context()->source(); + pmtime->addArchive(source.start(), source.end(), + source.timezone(), source.host(), true); + } + } + + // If all is well, we can now create the new "Record" View. + // Order of cleanup and changing Record mode state is different + // in the error case to non-error case, this is important for + // getting the window state correct (i.e. pmview->enableUi()). + + if (error) { + cleanupRecording(); + pmview->setRecordState(false); + QMessageBox::warning(NULL, pmProgname, errmsg, + QMessageBox::Ok|QMessageBox::Default|QMessageBox::Escape, + QMessageBox::NoButton, QMessageBox::NoButton); + } + else { + // Make the current View stop recording before changing Views + pmview->setRecordState(false); + + View *view = new View; + console->post("View::stopRecording creating view: delta=%.2f pos=%.2f", + App::timevalToSeconds(*pmtime->archiveInterval()), + App::timevalToSeconds(*pmtime->archivePosition())); + // TODO: may need to update archive samples/visible? + view->init(archiveGroup, pmview->viewMenu(), "Record"); + pmview->addActiveView(view); + OpenViewDialog::openView((const char *)ViewControl::view().toAscii()); + cleanupRecording(); + } + return error; +#else + return false; +#endif +} + +bool View::queryRecording(void) +{ + QString errmsg; + bool error = QedViewControl::queryRecording(errmsg); + + if (error) { + pmview->setRecordState(false); + QMessageBox::warning(NULL, pmProgname, errmsg, + QMessageBox::Ok|QMessageBox::Default|QMessageBox::Escape, + QMessageBox::NoButton, QMessageBox::NoButton); + } + return error; +} + +bool View::detachLoggers(void) +{ + QString errmsg; + bool error = QedViewControl::detachLoggers(errmsg); + + if (error) { + pmview->setRecordState(false); + QMessageBox::warning(NULL, pmProgname, errmsg, + QMessageBox::Ok|QMessageBox::Default|QMessageBox::Escape, + QMessageBox::NoButton, QMessageBox::NoButton); + } + else { + pmview->setRecordState(false); + cleanupRecording(); + } + return error; +} diff --git a/src/pmview/pmview.desktop b/src/pmview/pmview.desktop new file mode 100644 index 0000000..4129bcd --- /dev/null +++ b/src/pmview/pmview.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=PCP View +Comment=3D tool for displaying Performance Co-Pilot metrics +Exec=pmview +Icon=pmview +Terminal=false +Type=Application +Categories=System;Utility;Network;Qt; diff --git a/src/pmview/pmview.h b/src/pmview/pmview.h new file mode 100644 index 0000000..bd402ad --- /dev/null +++ b/src/pmview/pmview.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2007-2009, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef PMVIEW_H +#define PMVIEW_H + +#include <Inventor/Qt/SoQt.h> +#include <Inventor/Qt/viewers/SoQtExaminerViewer.h> +#include <Inventor/nodes/SoDrawStyle.h> + +#include "ui_pmview.h" + +#include "qmc_time.h" +#include "qed_statusbar.h" +#include "qed_viewcontrol.h" + +class ModList; +class SceneGroup; + +class View : public QedViewControl +{ +public: + View() : QedViewControl() { }; + virtual ~View() { }; + + void init(SceneGroup *, QMenu *, QString); + SceneGroup *group() const { return my.group; } + + bool saveConfig(QString, bool, bool, bool, bool); + QStringList hostList(bool); + QString pmloggerSyntax(bool); + + bool stopRecording(); + bool queryRecording(); + bool detachLoggers(); + +private: + struct { + SceneGroup *group; + } my; +}; + +class PmView : public QMainWindow, public Ui::PmView +{ + Q_OBJECT + +public: + PmView(); + + typedef enum { + nothing = 0, + // fetch = 0x1, -- TODO + metrics = 0x2, + inventor = 0x4, + metricLabel = 0x8, + timeLabel = 0x10, + all = 0xffffffff, + } RenderOptions; + + static int defaultFontSize(); + static double defaultViewDelta() { return 1.0; } // seconds + static double defaultLoggerDelta() { return 1.0; } + static int defaultTimeout() { return 3000; } // milliseconds + static int minimumPoints() { return 2; } + static int maximumPoints() { return 360; } + static int maximumLegendLength() { return 120; } // chars + static int minimumViewHeight() { return 80; } // pixels + + bool view(bool, float, float, float, float, float); + void render(RenderOptions options, time_t); + View *activeView() { return my.viewList.at(my.activeView); } + bool isViewRecording(); + bool isArchiveView(); + + virtual void step(bool livemode, QmcTime::Packet *pmtime); + virtual void VCRMode(bool livemode, QmcTime::Packet *pmtime, bool drag); + virtual void timeZone(bool livemode, QmcTime::Packet *pmtime, char *tzdata); + virtual void setDateLabel(QString label); + virtual void setDateLabel(time_t seconds, QString tz); + virtual void setButtonState(QedTimeButton::State state); + virtual void setRecordState(bool recording); + + virtual QMenu *createPopupMenu(); + virtual void updateToolbarContents(); + virtual void updateToolbarLocation(); + virtual QList<QAction*> toolbarActionsList(); + virtual QList<QAction*> enabledActionsList(); + virtual void setupEnabledActionsList(); + virtual void addSeparatorAction(); + virtual void setEnabledActionsList(QStringList tools, bool redisplay); + + // Adjusted height for exporting images (without UI elements) + int exportHeight() + { return height() - menuBar()->height() - toolBar->height(); } + + SoQtExaminerViewer *viewer() { return my.viewer; } + static void selectionCB(ModList *, bool); + +public slots: + virtual void init(); + virtual void quit(); + virtual void enableUi(); + virtual void filePrint(); + virtual void fileQuit(); + virtual void helpManual(); + virtual void helpTutorial(); + virtual void helpAbout(); + virtual void helpSeeAlso(); + virtual void whatsThis(); + virtual void optionsNewPmchart(); + virtual void optionsTimeControl(); + virtual void optionsMenubar(); + virtual void optionsToolbar(); + virtual void optionsConsole(); + virtual void recordStart(); + virtual void recordQuery(); + virtual void recordStop(); + virtual void recordDetach(); + virtual void updateToolbarOrientation(Qt::Orientation); + +protected slots: + virtual void languageChange(); + virtual void closeEvent(QCloseEvent *); + +private: + struct { + bool dialogsSetup; + bool liveHidden; + bool archiveHidden; + bool menubarHidden; + bool toolbarHidden; + bool consoleHidden; + + QMenu *viewMenu; + QList<QAction*> separatorsList; // separator follow these + QList<QAction*> toolbarActionsList; // all toolbar actions + QList<QAction*> enabledActionsList; // currently visible actions + + QList<View *>viewList; + int activeView; + + SoSeparator *root; + SoDrawStyle *drawStyle; + SoQtExaminerViewer *viewer; // The examiner window + + QString text; + QString prevText; + QedStatusBar *statusBar; + } my; +}; + +#endif // PMVIEW_H diff --git a/src/pmview/pmview.info.in b/src/pmview/pmview.info.in new file mode 100644 index 0000000..bfa34df --- /dev/null +++ b/src/pmview/pmview.info.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleIconFile</key> + <string>pmview.icns</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>@pkg_version@</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleExecutable</key> + <string>pmview</string> + <key>CFBundleIdentifier</key> + <string>com.aconex.pmview</string> +</dict> +</plist> diff --git a/src/pmview/pmview.pro b/src/pmview/pmview.pro new file mode 100644 index 0000000..e546eba --- /dev/null +++ b/src/pmview/pmview.pro @@ -0,0 +1,34 @@ +TEMPLATE = app +LANGUAGE = C++ +HEADERS = main.h pmview.h colorlist.h \ + barmod.h barobj.h baseobj.h \ + defaultobj.h gridobj.h labelobj.h stackobj.h \ + launch.h viewobj.h pipeobj.h link.h xing.h \ + scenefileobj.h scenegroup.h \ + colorscalemod.h colormod.h colorscale.h \ + metriclist.h modlist.h modulate.h \ + scalemod.h stackmod.h togglemod.h \ + text.h yscalemod.h pcpcolor.h +SOURCES = main.cpp colorlist.cpp barmod.cpp barobj.cpp baseobj.cpp \ + defaultobj.cpp gridobj.cpp labelobj.cpp stackobj.cpp \ + launch.cpp viewobj.cpp pipeobj.cpp link.cpp xing.cpp \ + scenefileobj.cpp scenegroup.cpp \ + colorscalemod.cpp colormod.cpp colorscale.cpp \ + scalemod.cpp stackmod.cpp togglemod.cpp yscalemod.cpp \ + metricList.cpp modlist.cpp modulate.cpp pcpcolor.cpp \ + text.cpp error.cpp gram.cpp lex.cpp pmview.cpp +FORMS = pmview.ui +ICON = pmview.icns +RC_FILE = pmview.rc +RESOURCES = pmview.qrc +INCLUDEPATH += /usr/include/Coin2 +INCLUDEPATH += ../include ../libpcp_qmc/src ../libpcp_qed/src +CONFIG += qt warn_on +LIBS += -L../libpcp/src +LIBS += -L../libpcp_qmc/src -L../libpcp_qmc/src/$$DESTDIR +LIBS += -L../libpcp_qed/src -L../libpcp_qed/src/$$DESTDIR +LIBS += -lpcp_qed -lpcp_qmc -lpcp -lCoin -lSoQt +win32:LIBS += -lwsock32 +QT += network +QMAKE_INFO_PLIST = pmview.info +QMAKE_CXXFLAGS += $$(PCP_CFLAGS) diff --git a/src/pmview/pmview.qrc b/src/pmview/pmview.qrc new file mode 100644 index 0000000..9694fc1 --- /dev/null +++ b/src/pmview/pmview.qrc @@ -0,0 +1,23 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/aboutpcp.png</file> + <file>images/aboutqt.png</file> + <file>images/pmview.png</file> + <file>images/pmtime.png</file> + <file>images/whatsthis.png</file> + <file>images/document-print.png</file> + <file>images/help-browser.png</file> + <file>images/help-contents.png</file> + <file>images/play_live.png</file> + <file>images/stop_live.png</file> + <file>images/play_record.png</file> + <file>images/stop_record.png</file> + <file>images/play_archive.png</file> + <file>images/stop_archive.png</file> + <file>images/back_archive.png</file> + <file>images/stepfwd_archive.png</file> + <file>images/stepback_archive.png</file> + <file>images/fastfwd_archive.png</file> + <file>images/fastback_archive.png</file> +</qresource> +</RCC> diff --git a/src/pmview/pmview.ui b/src/pmview/pmview.ui new file mode 100644 index 0000000..ed4fafa --- /dev/null +++ b/src/pmview/pmview.ui @@ -0,0 +1,386 @@ +<ui version="4.0" > + <class>PmView</class> + <widget class="QMainWindow" name="PmView" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>470</width> + <height>470</height> + </rect> + </property> + <property name="minimumSize" > + <size> + <width>182</width> + <height>196</height> + </size> + </property> + <property name="windowTitle" > + <string>PCP Viewer</string> + </property> + <property name="windowIcon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/pmview.png</normaloff>:/images/pmview.png</iconset> + </property> + <property name="iconSize" > + <size> + <width>22</width> + <height>22</height> + </size> + </property> + <widget class="QWidget" name="widget" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>0</number> + </property> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>370</width> + <height>24</height> + </rect> + </property> + <widget class="QMenu" name="Help" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpManualAction" /> + <addaction name="helpTutorialAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + <addaction name="helpSeeAlsoAction" /> + <addaction name="separator" /> + <addaction name="helpWhatsThisAction" /> + </widget> + <widget class="QMenu" name="Options" > + <property name="title" > + <string>&Options</string> + </property> + <addaction name="timeControlAction" /> + <addaction name="menubarAction" /> + <addaction name="toolbarAction" /> + <addaction name="consoleAction" /> + </widget> + <widget class="QMenu" name="Record" > + <property name="title" > + <string>&Record</string> + </property> + <addaction name="recordStartAction" /> + <addaction name="recordQueryAction" /> + <addaction name="recordStopAction" /> + <addaction name="separator" /> + <addaction name="recordDetachAction" /> + </widget> + <widget class="QMenu" name="File" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileQuitAction" /> + </widget> + <addaction name="File" /> + <addaction name="Record" /> + <addaction name="Options" /> + <addaction name="separator" /> + <addaction name="Help" /> + </widget> + <widget class="QToolBar" name="toolBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>24</y> + <width>370</width> + <height>36</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="windowTitle" > + <string>Toolbar</string> + </property> + <property name="whatsThis" > + <string>Configurable toolbar, use the Preferences dialog to change its contents</string> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <attribute name="toolBarArea" > + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak" > + <bool>false</bool> + </attribute> + <addaction name="filePrintAction" /> + </widget> + <action name="filePrintAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/document-print.png</normaloff>:/images/document-print.png</iconset> + </property> + <property name="text" > + <string>&Print...</string> + </property> + <property name="iconText" > + <string>Print</string> + </property> + <property name="shortcut" > + <string>Ctrl+P</string> + </property> + </action> + <action name="fileQuitAction" > + <property name="text" > + <string>&Quit</string> + </property> + <property name="iconText" > + <string>Quit</string> + </property> + <property name="shortcut" > + <string>Ctrl+Q</string> + </property> + </action> + <action name="timeControlAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/pmtime.png</normaloff>:/images/pmtime.png</iconset> + </property> + <property name="text" > + <string>Time Controls</string> + </property> + </action> + <action name="menubarAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="checked" > + <bool>true</bool> + </property> + <property name="text" > + <string>Menubar</string> + </property> + </action> + <action name="toolbarAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Toolbar</string> + </property> + </action> + <action name="consoleAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Console</string> + </property> + </action> + <action name="helpManualAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/help-contents.png</normaloff>:/images/help-contents.png</iconset> + </property> + <property name="text" > + <string>&Manual</string> + </property> + <property name="iconText" > + <string>Manual</string> + </property> + <property name="shortcut" > + <string>F1</string> + </property> + </action> + <action name="helpTutorialAction" > + <property name="text" > + <string>Tutorial</string> + </property> + <property name="iconText" > + <string>Tutorial</string> + </property> + </action> + <action name="helpAboutAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/pmview.png</normaloff>:/images/pmview.png</iconset> + </property> + <property name="text" > + <string>About</string> + </property> + <property name="iconText" > + <string>About</string> + </property> + <property name="shortcut" > + <string/> + </property> + </action> + <action name="helpSeeAlsoAction" > + <property name="text" > + <string>See Also</string> + </property> + <property name="iconText" > + <string>See Also</string> + </property> + </action> + <action name="helpWhatsThisAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/whatsthis.png</normaloff>:/images/whatsthis.png</iconset> + </property> + <property name="text" > + <string>What's This</string> + </property> + <property name="iconText" > + <string>What's This</string> + </property> + <property name="shortcut" > + <string>Shift+F1</string> + </property> + </action> + <action name="recordStartAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/camera-video.png</normaloff>:/images/camera-video.png</iconset> + </property> + <property name="text" > + <string>Start...</string> + </property> + </action> + <action name="recordQueryAction" > + <property name="text" > + <string>Query</string> + </property> + </action> + <action name="recordStopAction" > + <property name="icon" > + <iconset resource="pmview.qrc" > + <normaloff>:/images/camera-video-close.png</normaloff>:/images/camera-video-close.png</iconset> + </property> + <property name="text" > + <string>Stop</string> + </property> + </action> + <action name="recordDetachAction" > + <property name="text" > + <string>Detach</string> + </property> + </action> + </widget> + <includes> + <include location="local" >qmc_time.h</include> + <include location="local" >qprinter.h</include> + <include location="local" >qed_timebutton.h</include> + </includes> + <resources> + <include location="pmview.qrc" /> + </resources> + <connections> + <connection> + <sender>filePrintAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>filePrint()</slot> + </connection> + <connection> + <sender>fileQuitAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>fileQuit()</slot> + </connection> + <connection> + <sender>helpTutorialAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>helpTutorial()</slot> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>helpSeeAlsoAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>helpSeeAlso()</slot> + </connection> + <connection> + <sender>helpWhatsThisAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>whatsThis()</slot> + </connection> + <connection> + <sender>timeControlAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>optionsTimeControl()</slot> + </connection> + <connection> + <sender>menubarAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>optionsMenubar()</slot> + </connection> + <connection> + <sender>toolbarAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>optionsToolbar()</slot> + </connection> + <connection> + <sender>consoleAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>optionsConsole()</slot> + </connection> + <connection> + <sender>helpManualAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>helpManual()</slot> + </connection> + <connection> + <sender>recordStartAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>recordStart()</slot> + </connection> + <connection> + <sender>recordQueryAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>recordQuery()</slot> + </connection> + <connection> + <sender>recordStopAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>recordStop()</slot> + </connection> + <connection> + <sender>recordDetachAction</sender> + <signal>triggered()</signal> + <receiver>PmView</receiver> + <slot>recordDetach()</slot> + </connection> + </connections> +</ui> diff --git a/src/pmview/scalemod.cpp b/src/pmview/scalemod.cpp new file mode 100644 index 0000000..49c7a36 --- /dev/null +++ b/src/pmview/scalemod.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include "main.h" +#include "scalemod.h" +#include "modlist.h" +#include "launch.h" + +#include <iostream> +using namespace std; + +ScaleMod::~ScaleMod() +{ +} + +ScaleMod::ScaleMod(const char *str, + double scale, + const SbColor &color, + SoNode *obj, + float xScale, + float yScale, + float zScale) +: Modulate(str, scale, color), + _state(Modulate::start), + _color(0), + _scale(0), + _xScale(xScale), + _yScale(yScale), + _zScale(zScale) +{ + _root = new SoSeparator; + + _color = new SoBaseColor; + _color->rgb.setValue(_errorColor.getValue()); + _root->addChild(_color); + + if (_metrics->numValues() == 1 && status() >= 0) { + + _scale = new SoScale; + _scale->scaleFactor.setValue(1.0, 1.0, 1.0); + _root->addChild(_scale); + _root->addChild(obj); + + add(); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ScaleMod: Added " << str << " (Id = " + << _root->getName().getString() << ")" << endl; +#endif + } + + // Invalid metric + else + _root->addChild(obj); +} + +void +ScaleMod::refresh(bool fetchFlag) +{ + QmcMetric &metric = _metrics->metric(0); + + if (status() < 0) + return; + + if (fetchFlag) + metric.update(); + + if (metric.error(0) <= 0) { + if (_state != Modulate::error) { + _color->rgb.setValue(_errorColor.getValue()); + _scale->scaleFactor.setValue((_xScale==0.0f ? 1.0 : theMinScale), + (_yScale==0.0f ? 1.0 : theMinScale), + (_zScale==0.0f ? 1.0 : theMinScale)); + _state = Modulate::error; + } + } + else { + double value = metric.value(0) * theScale; + if (value > theNormError) { + if (_state != Modulate::saturated) { + _color->rgb.setValue(_saturatedColor.getValue()); + _scale->scaleFactor.setValue(1.0, 1.0, 1.0); + _state = Modulate::saturated; + } + } + else { + if (_state != Modulate::normal) { + _color->rgb.setValue(_metrics->color(0).getValue()); + _state = Modulate::normal; + } + if (value < Modulate::theMinScale) + value = Modulate::theMinScale; + else if (value > 1.0) + value = 1.0; + _scale->scaleFactor.setValue((_xScale==0.0f ? 1.0 : _xScale*value), + (_yScale==0.0f ? 1.0 : _yScale*value), + (_zScale==0.0f ? 1.0 : _zScale*value)); + } + } +} + +void +ScaleMod::dump(QTextStream &os) const +{ + os << "ScaleMod: "; + + if (status() < 0) + os << "Invalid metric"; + else { + os << "state = "; + dumpState(os, _state); + os << ", scale = " << _xScale << ',' << _yScale << ',' << _zScale + << ": "; + _metrics->metric(0).dump(os, true); + } +} + +void +ScaleMod::infoText(QString &str, bool) const +{ + const QmcMetric &metric = _metrics->metric(0); + str = metric.spec(true, true, 0); + str.append(QChar('\n')); + if (_state == Modulate::error) + str.append(theErrorText); + else if (_state == Modulate::start) + str.append(theStartText); + else { + QString value; + str.append(value.setNum(metric.realValue(0), 'g', 4)); + str.append(QChar(' ')); + if (metric.desc().units().size() > 0) + str.append(metric.desc().units()); + str.append(" ["); + str.append(value.setNum(metric.value(0) * 100.0, 'g', 4)); + str.append("% of expected max]"); + } +} + +void +ScaleMod::launch(Launch &launch, bool) const +{ + if (status() < 0) + return; + launch.startGroup("point"); + launch.addMetric(_metrics->metric(0), _metrics->color(0), 0); + launch.endGroup(); +} + +int +ScaleMod::select(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ScaleMod::select: " << _metrics->metric(0) << endl; +#endif + return 1; +} + +int +ScaleMod::remove(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ScaleMod::remove: " << _metrics->metric(0) << endl; +#endif + return 0; +} diff --git a/src/pmview/scalemod.h b/src/pmview/scalemod.h new file mode 100644 index 0000000..2a28adc --- /dev/null +++ b/src/pmview/scalemod.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _SCALEMOD_H_ +#define _SCALEMOD_H_ + +#include "modulate.h" + +class SoBaseColor; +class SoScale; +class SoNode; +class Launch; + +class ScaleMod : public Modulate +{ +protected: + + State _state; + SoBaseColor *_color; + SoScale *_scale; + float _xScale; + float _yScale; + float _zScale; + +public: + + virtual ~ScaleMod(); + + ScaleMod(const char *metric, double scale, const SbColor &color, + SoNode *obj, float xScale, float yScale, float zScale); + + virtual void refresh(bool fetchFlag); + + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void infoText(QString &str, bool) const; + + virtual void launch(Launch &launch, bool) const; + + virtual void dump(QTextStream &) const; + +private: + + ScaleMod(); + ScaleMod(const ScaleMod &); + const ScaleMod &operator=(const ScaleMod &); + // Never defined +}; + +#endif /* _SCALEMOD_H_ */ diff --git a/src/pmview/scenefileobj.cpp b/src/pmview/scenefileobj.cpp new file mode 100644 index 0000000..76d7446 --- /dev/null +++ b/src/pmview/scenefileobj.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoRotationXYZ.h> +#include <Inventor/nodes/SoCylinder.h> +#include <Inventor/nodes/SoCube.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> + +#include <ctype.h> +#include "stackmod.h" +#include "togglemod.h" +#include "scenefileobj.h" +#include "defaultobj.h" +#include "colorlist.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Constructor for Generic Inventor Scene Object. +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +SceneFileObj::SceneFileObj (const DefaultObj & defs, + int c, int r, + int colSpan, int rowSpan, + BaseObj::Alignment align) + : ModObj (false, defs, c, r, colSpan, rowSpan, center) + , _align(align) + , _tag ("\n") +{ + _objtype |= SCENEFILEOBJ; + + for ( int i=0; i < 3; i++) { + _color[i] = defs.baseColor(i); + } + + cellHeight = defs.baseHeight(); + _stackHeight = defs.pipeLength(); + + if ( _align == north || _align == south ) { + cellWidth = defs.baseHeight(); + cellDepth = (defs.pipeLength() < cellHeight) ? + cellHeight : defs.pipeLength(); + } else { + cellWidth = (defs.pipeLength() < cellHeight) ? + cellHeight : defs.pipeLength(); + cellDepth = defs.baseHeight(); + } + + _width = colSpan * cellWidth; + _depth = rowSpan * cellDepth; + _sceneFileName[0] = '\0'; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void +SceneFileObj::finishedAdd () +{ + SoSeparator *sp; + + BaseObj::addBase (_root); + if ((sp = readSceneFile())) { + _root->addChild (sp); + // To make sure the viewer will display a scene with a single + // IV scenegraph treat all IV scenegraphs as "modulated" objects. + ViewObj::theNumModObjects++; + } else { + pmprintf("Warning: Failed to read scene file \"%s\"\n", _sceneFileName); + } +} + +void +SceneFileObj::setTag (const char * s) +{ + _tag = s; + if ( strchr (s, '\n' ) == NULL ) { + _tag.append ("\n"); + } +} + +SoSeparator * +SceneFileObj::readSceneFile(void) +{ + SoInput input; + SoSeparator *s; + FILE *f = NULL; + + if (_sceneFileName[0] == '<') { + char *p = _sceneFileName+1; + + while (*p) { + if (! isdigit(*p)) + break; + p++; + } + + if (*p == '\0') { + int fd = atoi(_sceneFileName+1); + + if ((f = fdopen(fd, "r")) == NULL) { + return (NULL); + } + input.setFilePointer(f); + } + } + + if (f == NULL) { + if (!input.openFile(_sceneFileName)) + return NULL; + } + s = SoDB::readAll(&input); + input.closeFile(); + + return s; +} diff --git a/src/pmview/scenefileobj.h b/src/pmview/scenefileobj.h new file mode 100644 index 0000000..0ddb1f5 --- /dev/null +++ b/src/pmview/scenefileobj.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _SCENEFILEOBJ_H_ +#define _SCENEFILEOBJ_H_ + +#include <Inventor/SoLists.h> +#include "modobj.h" +#include "modlist.h" + +class SceneFileObj : public ModObj +{ +public: + virtual ~SceneFileObj () {} + SceneFileObj(const DefaultObj &, int, int, int, int, Alignment); + + int width () const { return _width; } + int depth () const { return _depth; } + + const char * name() const { return "SceneFile"; } + SoSeparator *readSceneFile(void); + void setSceneFileName(char *fname) { strcpy(_sceneFileName, fname); }; + + void finishedAdd (); + void setTag (const char *); + +private: + SceneFileObj (); + SceneFileObj (SceneFileObj const &); + SceneFileObj const& operator=(SceneFileObj const &); + + int _width, _depth; + int cellWidth, cellDepth, cellHeight; + Alignment _align; + float _stackHeight; + QString _tag; + + float _color[3]; + char _sceneFileName[MAXPATHLEN]; +}; + +#endif /* _SCENEFILEOBJ_H_ */ diff --git a/src/pmview/scenegroup.cpp b/src/pmview/scenegroup.cpp new file mode 100644 index 0000000..810c089 --- /dev/null +++ b/src/pmview/scenegroup.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2009, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "main.h" +#include "pmview.h" +#include "scenegroup.h" +#include "qed_console.h" +#include "qed_timecontrol.h" + +SceneGroup::SceneGroup() : QedGroupControl() +{ +} + +SceneGroup::~SceneGroup() +{ +} + +void SceneGroup::init(struct timeval *interval, struct timeval *position) +{ + QedGroupControl::init(interval, position); +} + +bool SceneGroup::isArchiveSource(void) +{ + // Note: We purposefully are not using QmcGroup::mode() here, as we + // may not have initialised any contexts yet. In such a case, live + // mode is always returned (default, from the QmcGroup constructor). + + return this == archiveGroup; +} + +bool SceneGroup::isActive(QmcTime::Packet *packet) +{ + return (((activeGroup == archiveGroup) && + (packet->source == QmcTime::ArchiveSource)) || + ((activeGroup == liveGroup) && + (packet->source == QmcTime::HostSource))); +} + +bool SceneGroup::isRecording(QmcTime::Packet *packet) +{ + (void)packet; + return pmview->isViewRecording(); +} + +void SceneGroup::setButtonState(QedTimeButton::State state) +{ + pmview->setButtonState(state); +} + +void SceneGroup::updateTimeAxis(void) +{ + QString tz, otz, unused; + + if (numContexts() > 0 || isArchiveSource() == false) { + if (numContexts() > 0) + defaultTZ(unused, otz); + else + otz = QmcSource::localHost; + tz = otz; + pmview->setDateLabel((int)timePosition(), tz); + } else { + pmview->setDateLabel(tr("[No open archives]")); + } + + if (console->logLevel(QedApp::DebugProtocol)) { + console->post(QedApp::DebugProtocol, + "SceneGroup::updateTimeAxis: tz=%s", + (const char *)tz.toAscii()); + } +} + +void SceneGroup::updateTimeButton(void) +{ + pmview->setButtonState(buttonState()); +} + +void SceneGroup::setupWorldView() +{ + activeGroup->QedGroupControl::setupWorldView( + pmtime->archiveInterval(), pmtime->archivePosition(), + pmtime->archiveStart(), pmtime->archiveEnd()); +} + +void SceneGroup::adjustLiveWorldViewForward(QmcTime::Packet *packet) +{ + double position = timePosition(); + + console->post("Fetching data at %s", QedApp::timeString(position)); + fetch(); + + setTimeState(packet->state == QmcTime::StoppedState ? + StandbyState : ForwardState); + + QedGroupControl::adjustLiveWorldViewForward(packet); + pmview->render(PmView::inventor, 0); +} + +void SceneGroup::adjustArchiveWorldViewForward(QmcTime::Packet *packet, bool setup) +{ + console->post("SceneGroup::adjustArchiveWorldViewForward"); + setTimeState(ForwardState); + + int setmode = PM_MODE_INTERP; + int delta = packet->delta.tv_sec; + if (packet->delta.tv_usec == 0) { + setmode |= PM_XTB_SET(PM_TIME_SEC); + } else { + delta = delta * 1000 + packet->delta.tv_usec / 1000; + setmode |= PM_XTB_SET(PM_TIME_MSEC); + } + + struct timeval timeval; + double position = timePosition(); + QedApp::timevalFromSeconds(timePosition(), &timeval); + setArchiveMode(setmode, &timeval, delta); + console->post("Fetching data at %s", QedApp::timeString(position)); + fetch(); + + QedGroupControl::adjustArchiveWorldViewForward(packet, setup); + pmview->render(PmView::inventor, 0); +} + +void SceneGroup::adjustArchiveWorldViewBackward(QmcTime::Packet *packet, bool setup) +{ + console->post("SceneGroup::adjustArchiveWorldViewBackward"); + setTimeState(BackwardState); + + int setmode = PM_MODE_INTERP; + int delta = packet->delta.tv_sec; + if (packet->delta.tv_usec == 0) { + setmode |= PM_XTB_SET(PM_TIME_SEC); + } else { + delta = delta * 1000 + packet->delta.tv_usec / 1000; + setmode |= PM_XTB_SET(PM_TIME_MSEC); + } + + struct timeval timeval; + double position = timePosition(); + QedApp::timevalFromSeconds(timePosition(), &timeval); + setArchiveMode(setmode, &timeval, delta); + console->post("Fetching data at %s", QedApp::timeString(position)); + fetch(); + + QedGroupControl::adjustArchiveWorldViewBackward(packet, setup); + pmview->render(PmView::inventor, 0); +} + +// +// Fetch all metric values across all scenes, and update the status bar. +// +void SceneGroup::adjustStep(QmcTime::Packet *packet) +{ + (void)packet; // no-op in pmview +} + +void SceneGroup::step(QmcTime::Packet *packet) +{ + QedGroupControl::step(packet); + pmview->render(PmView::inventor, 0); +} + +void SceneGroup::setTimezone(QmcTime::Packet *packet, char *tz) +{ + QedGroupControl::setTimezone(packet, tz); + if (isActive(packet)) + updateTimeAxis(); +} diff --git a/src/pmview/scenegroup.h b/src/pmview/scenegroup.h new file mode 100644 index 0000000..0e40697 --- /dev/null +++ b/src/pmview/scenegroup.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef SCENEGROUP_H +#define SCENEGROUP_H + +#include <QtCore/QList> +#include <QtGui/QColor> +#include <QtGui/QPainter> +#include <QtGui/QDockWidget> +#include <QtGui/QAbstractButton> +#include "qed_groupcontrol.h" +#include "qmc_metric.h" +#include "qmc_group.h" +#include "qmc_time.h" + +class SceneGroup : public QedGroupControl +{ + Q_OBJECT + +public: + SceneGroup(); + virtual ~SceneGroup(); + void init(struct timeval *, struct timeval *); + + bool isArchiveSource(); + bool isActive(QmcTime::Packet *); + bool isRecording(QmcTime::Packet *); + + void updateTimeAxis(); + void updateTimeButton(); + + void setupWorldView(); + void step(QmcTime::Packet *); + void setTimezone(QmcTime::Packet *, char *); + +protected: + void adjustLiveWorldViewForward(QmcTime::Packet *); + void adjustArchiveWorldViewForward(QmcTime::Packet *, bool); + void adjustArchiveWorldViewBackward(QmcTime::Packet *, bool); + + void adjustStep(QmcTime::Packet *); + void setButtonState(QedTimeButton::State); + +private: + void refreshScenes(bool); + +// struct { +// } my; +}; + +#endif // SCENEGROUP_H diff --git a/src/pmview/stackmod.cpp b/src/pmview/stackmod.cpp new file mode 100644 index 0000000..448b9bb --- /dev/null +++ b/src/pmview/stackmod.cpp @@ -0,0 +1,594 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/SoPath.h> +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoSelection.h> +#include <Inventor/nodes/SoSwitch.h> +#include "stackmod.h" +#include "modlist.h" +#include "launch.h" + +#include <iostream> +using namespace std; + +// +// Use debug flag LIBPMDA to trace stack refreshes +// + +const float StackMod::theDefFillColor[] = { 0.35, 0.35, 0.35 }; +const char StackMod::theStackId = 's'; + +StackMod::~StackMod() +{ +} + +StackMod::StackMod(MetricList *metrics, SoNode *obj, StackMod::Height height) +: Modulate(metrics), + _blocks(), + _switch(0), + _height(height), + _text(), + _selectCount(0), + _infoValue(0), + _infoMetric(0), + _infoInst(0) +{ + int numValues = _metrics->numValues(); + int numMetrics = _metrics->numMetrics(); + char buf[32]; + float initScale = 0.0; + int m, i, v; + + _root = new SoSeparator; + + if (numValues > 0) { + m = numValues; + if (_height == fixed) { + m++; + _text.append(QChar('\n')); + } + + _blocks.resize(m); + _infoValue = m+1; + + initScale = 1.0 / (float)numValues; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::StackMod: numValues = " + << numValues << ", num of blocks = " << m << endl + << *_metrics; +#endif + + for (m = 0, v = 0; m < numMetrics; m++) { + const QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + StackBlock block; + + block._sep = new SoSeparator; + sprintf(buf, "%c%d", theStackId, v); + block._sep->setName((SbName)buf); + _root->addChild(block._sep); + + block._color = new SoBaseColor; + block._color->rgb.setValue(_errorColor.getValue()); + block._sep->addChild(block._color); + + block._scale = new SoScale; + block._scale->scaleFactor.setValue(1.0, initScale, 1.0); + block._sep->addChild(block._scale); + + block._sep->addChild(obj); + + block._state = Modulate::start; + block._selected = false; + + if (_height == fixed || v < numValues - 1) { + block._tran = new SoTranslation(); + block._tran->translation.setValue(0.0, initScale, 0.0); + _root->addChild(block._tran); + } + else { + block._tran = NULL; + } + _blocks[v] = block; + } + } + + if (_height == fixed) { + StackBlock block; + block._sep = new SoSeparator; + _root->addChild(block._sep); + sprintf(buf, "%c%d", theStackId, v); + block._sep->setName((SbName)buf); + + _switch = new SoSwitch(); + _switch->whichChild.setValue(SO_SWITCH_ALL); + block._sep->addChild(_switch); + + block._color = new SoBaseColor; + block._color->rgb.setValue(theDefFillColor); + _switch->addChild(block._color); + + block._tran = NULL; + block._scale = new SoScale; + block._scale->scaleFactor.setValue(1.0, 0.0, 1.0); + block._state = Modulate::start; + block._selected = false; + _switch->addChild(block._scale); + + _switch->addChild(obj); + _blocks[v] = block; + } + + add(); + } + + // Invalid object + else { + + _sts = -1; + + SoBaseColor *tmpColor = new SoBaseColor(); + tmpColor->rgb.setValue(_errorColor.getValue()); + _root->addChild(tmpColor); + + _root->addChild(obj); + } +} + +void +StackMod::refresh(bool fetchFlag) +{ + int numValues = _metrics->numValues(); + int numMetrics = _metrics->numMetrics(); + int m, i, v; + double sum = 0.0; + + static QVector<double> values; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << endl << "StackMod::refresh" << endl; +#endif + + if (status() < 0) + return; + + if (numValues > values.size()) + values.resize(numValues); + + for (m = 0, v = 0; m < numMetrics; m++) { + QmcMetric &metric = _metrics->metric(m); + if (fetchFlag) + metric.update(); + for (i = 0; i < metric.numValues(); i++, v++) { + + StackBlock &block = _blocks[v]; + double &value = values[v]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << '[' << v << "] "; +#endif + + if (metric.error(i) <= 0) { + if (block._state != Modulate::error) { + block._color->rgb.setValue(_errorColor.getValue()); + block._state = Modulate::error; + } + value = Modulate::theMinScale; + sum += value; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << "Error, value set to " << value << endl; +#endif + + } + else if (block._state == Modulate::error || + block._state == Modulate::start) { + block._state = Modulate::normal; + if (numMetrics == 1) + block._color->rgb.setValue(_metrics->color(v).getValue()); + else + block._color->rgb.setValue(_metrics->color(m).getValue()); + value = metric.value(i) * theScale; + if (value < theMinScale) + value = theMinScale; + sum += value; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << "Error->Normal, value = " << value << endl; +#endif + + } + else { + value = metric.value(i) * theScale; + if (value < theMinScale) + value = theMinScale; + sum += value; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << "Normal, value = " << value << endl; +#endif + + } + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << "sum = " << sum << endl; +#endif + + if (sum > theNormError && _height != util) { + if (_blocks[0]._state != Modulate::saturated) { + for (v = 0; v < numValues; v++) { + StackBlock &block = _blocks[v]; + if (block._state != Modulate::error) { + block._color->rgb.setValue(Modulate::_saturatedColor); + block._state = Modulate::saturated; + } + } + } + } + else { + for (m = 0, v = 0; m < numMetrics; m++) { + QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + StackBlock &block = _blocks[v]; + if (block._state == Modulate::saturated) { + block._state = Modulate::normal; + if (numMetrics == 1) + block._color->rgb.setValue(_metrics->color(v).getValue()); + else + block._color->rgb.setValue(_metrics->color(m).getValue()); + } + } + } + } + + // Scale values to the range [0,1]. + // Ensure that each block always has the minimum height to + // avoid planes clashing + + if (sum > 1.0 || _height == util) { + double oldSum = sum; + double max = 1.0 - (theMinScale * (numValues - 1)); + sum = 0.0; + for (v = 0; v < numValues; v++) { + double &value = values[v]; + value /= oldSum; + sum += value; + if (sum > max) { + value -= sum - max; + sum -= sum - max; + } + if (value < theMinScale) + value = theMinScale; + max += theMinScale; + } + } + + for (v = 0; v < numValues; v++) { + + StackBlock &block = _blocks[v]; + double &value = values[v]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + cerr << '[' << v << "] scale = " << value << endl; +#endif + + block._scale->scaleFactor.setValue(1.0, value, 1.0); + + if (v < numValues-1 || _height == fixed) + block._tran->translation.setValue(0.0, value, 0.0); + } + + if (_height == fixed) { + sum = 1.0 - sum; + if (sum >= theMinScale) { + _switch->whichChild.setValue(SO_SWITCH_ALL); + _blocks[v]._scale->scaleFactor.setValue(1.0, sum, 1.0); + } + else { + _switch->whichChild.setValue(SO_SWITCH_NONE); + _blocks[v]._scale->scaleFactor.setValue(1.0, theMinScale, 1.0); + } + } +} + +void +StackMod::dump(QTextStream &os) const +{ + int m, i, v; + + os << "StackMod: "; + + if (status() < 0) + os << "Invalid metrics: " << pmErrStr(status()) << endl; + else { + os << endl; + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + os << " [" << v << "]: "; + if (_blocks[v]._selected == true) + os << '*'; + else + os << ' '; + dumpState(os, _blocks[v]._state); + os << ": "; + metric.dump(os, true, i); + } + } + } +} + +void +StackMod::infoText(QString &str, bool selected) const +{ + int m = _infoMetric; + int i = _infoInst; + int v = _infoValue; + bool found = false; + + if (selected && _selectCount == 1) { + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + const QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) + if (_blocks[v]._selected) { + found = true; + break; + } + if (found) + break; + } + } + + if (v >= _blocks.size()) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::infoText: infoText requested but nothing selected" + << endl; +#endif + str = ""; + } + else if (_height == fixed && v == _blocks.size() - 1) { + str = _text; + } + else { + const QmcMetric &metric = _metrics->metric(m); + str = metric.spec(true, true, i); + str.append(QChar('\n')); + + if (_blocks[v]._state == Modulate::error) + str.append(theErrorText); + else if (_blocks[v]._state == Modulate::start) + str.append(theStartText); + else { + QString value; + str.append(value.setNum(metric.realValue(i), 'g', 4)); + str.append(QChar(' ')); + if (metric.desc().units().length() > 0) + str.append(metric.desc().units()); + str.append(" ["); + str.append(value.setNum(metric.value(i) * 100.0, 'g', 4)); + str.append("% of expected max]"); + } + } +} + +void +StackMod::launch(Launch &launch, bool all) const +{ + int m, i, v; + bool launchAll = all; + + if (status() < 0) + return; + + // If the filler block is selected, launch all metrics + if (!launchAll && _height == fixed && + _blocks.last()._selected == true) { + launchAll = true; + } + + if (_height == StackMod::util) + launch.startGroup("util"); + else + launch.startGroup("stack"); + + for (m = 0, v = 0; m < _metrics->numMetrics(); m++) { + QmcMetric &metric = _metrics->metric(m); + for (i = 0; i < metric.numValues(); i++, v++) { + if ((_selectCount > 0 && _blocks[v]._selected == true) || + _selectCount == 0 || launchAll == true) { + + launch.addMetric(_metrics->metric(m), + _metrics->color(m), + i); + } + } + } + + launch.endGroup(); +} + +void +StackMod::selectAll() +{ + int i; + + if (_selectCount == _blocks.size()) + return; + + theModList->selectAllId(_root, _blocks.size()); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::selectAll" << endl; +#endif + + for (i = 0; i < _blocks.size(); i++) { + if (_blocks[i]._selected == false) { + _selectCount++; + theModList->selectSingle(_blocks[i]._sep); + _blocks[i]._selected = true; + } + } +} + +int +StackMod::select(SoPath *path) +{ + int metric, inst, value; + + findBlock(path, metric, inst, value, false); + if (value < _blocks.size() && _blocks[value]._selected == false) { + _blocks[value]._selected = true; + _selectCount++; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::select: value = " << value + << ", count = " << _selectCount << endl; +#endif + } + return _selectCount; +} + +int +StackMod::remove(SoPath *path) +{ + int metric, inst, value; + + findBlock(path, metric, inst, value, false); + if (value < _blocks.size() && _blocks[value]._selected == true) { + _blocks[value]._selected = false; + _selectCount--; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::remove: value = " << value + << ", count = " << _selectCount << endl; +#endif + + } + +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::remove: did not remove " << value + << ", count = " << _selectCount << endl; +#endif + + return _selectCount; +} + +void +StackMod::selectInfo(SoPath *path) +{ + findBlock(path, _infoMetric, _infoInst, _infoValue); +} + +void +StackMod::removeInfo(SoPath *) +{ + _infoValue = _blocks.size(); + _infoMetric = _infoInst = 0; +} + +void +StackMod::findBlock(SoPath *path, int &metric, int &inst, + int &value, bool idMetric) +{ + SoNode *node; + char *str; + int m, i, v; + char c; + + for (i = path->getLength() - 1; i >= 0; --i) { + node = path->getNode(i); + str = (char *)(node->getName().getString()); + if (strlen(str) && str[0] == theStackId) + break; + } + + if (i >= 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "StackMod::findBlock: stack id = " << str << endl; +#endif + + sscanf(str, "%c%d", &c, &value); + + if (value == 0 || idMetric == false) { + metric = 0; + inst = 0; + } + else { + m = 0; + v = value; + while (m < _metrics->numMetrics()) { + i = _metrics->metric(m).numValues(); + if (v < i) { + metric = m; + inst = v; + break; + } + else { + v -= i; + m++; + } + } + } + } + else { + value = _blocks.size(); + metric = inst = 0; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + cerr << "StackMod::findBlock: metric = " << metric + << ", inst = " << inst << ", value = " << value << endl; + } +#endif + + return; +} + +void +StackMod::setFillColor(const SbColor &col) +{ + if (_sts >= 0 && _height == fixed) + _blocks.last()._color->rgb.setValue(col.getValue()); +} + +void +StackMod::setFillColor(int packedcol) +{ + SbColor col; + float dummy = 0; + + col.setPackedValue(packedcol, dummy); + setFillColor(col); +} diff --git a/src/pmview/stackmod.h b/src/pmview/stackmod.h new file mode 100644 index 0000000..8c0fbc5 --- /dev/null +++ b/src/pmview/stackmod.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _STACKMOD_H_ +#define _STACKMOD_H_ + +#include <QtCore/QVector> +#include "modulate.h" + +class SoBaseColor; +class SoTranslation; +class SoScale; +class SoNode; +class SoSwitch; +class Launch; + +struct StackBlock { + SoSeparator *_sep; + SoBaseColor *_color; + SoScale *_scale; + SoTranslation *_tran; + Modulate::State _state; + bool _selected; +}; + +typedef QVector<StackBlock> StackBlockList; + +class StackMod : public Modulate +{ +public: + + enum Height { unfixed, fixed, util }; + +private: + + static const float theDefFillColor[]; + static const char theStackId; + + StackBlockList _blocks; + SoSwitch *_switch; + Height _height; + QString _text; + int _selectCount; + int _infoValue; + int _infoMetric; + int _infoInst; + +public: + + virtual ~StackMod(); + + StackMod(MetricList *metrics, + SoNode *obj, + Height height = unfixed); + + void setFillColor(const SbColor &col); + void setFillColor(int packedcol); + void setFillText(const char *str) + { _text = str; } + + virtual void refresh(bool fetchFlag); + + virtual void selectAll(); + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void selectInfo(SoPath *); + virtual void removeInfo(SoPath *); + + virtual void infoText(QString &str, bool) const; + + virtual void launch(Launch &launch, bool all) const; + + virtual void dump(QTextStream &) const; + +private: + + StackMod(); + StackMod(const StackMod &); + const StackMod &operator=(const StackMod &); + // Never defined + + void findBlock(SoPath *path, int &metric, int &inst, + int &value, bool idMetric = true); +}; + +#endif /* _STACKMOD_H_ */ diff --git a/src/pmview/stackobj.cpp b/src/pmview/stackobj.cpp new file mode 100644 index 0000000..faa8d42 --- /dev/null +++ b/src/pmview/stackobj.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoTransform.h> +#include "stackobj.h" +#include "colorlist.h" +#include "defaultobj.h" + +#include <iostream> +using namespace std; + +StackObj::~StackObj() +{ + delete _stack; +} + +StackObj::StackObj(StackMod::Height height, + ViewObj::Shape shape, + bool baseFlag, + const DefaultObj &defaults, + int x, int y, + int cols, int rows, + BaseObj::Alignment align) +: ModObj(baseFlag, defaults, x, y, cols, rows, align), + _width(0), + _depth(0), + _height(height), + _stack(0), + _text() +{ + _objtype |= STACKOBJ; + _shape = shape; +} + +void +StackObj::finishedAdd() +{ + int i; + + BaseObj::addBase(_root); + + if (_metrics.numMetrics() == 0) { + pmprintf("%s: Error: Stack object has no metrics\n", + pmProgname); + _length = 0; + } + else { + SoScale *blockScale = new SoScale(); + blockScale->scaleFactor.setValue(_length, _maxHeight, _length); + _root->addChild(blockScale); + + if (_metrics.numMetrics()) { + const char *colName = (const char *)_colors.toAscii(); + const ColorSpec *colSpec = theColorLists.list(colName); + if (colSpec != NULL) { + if (colSpec->_scale) + pmprintf("%s: Warning: Color scale ignored for stack object.\n", + pmProgname); + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "StackObj::finishedAdd: Adding " + << colSpec->_list.length() + << " colors for " << _metrics.numMetrics() + << " metrics" << endl; +#endif + + for (i = 0; i < colSpec->_list.size(); i++) + _metrics.add(*(colSpec->_list)[i]); + } + } + else + pmprintf("%s: Warning: No colours specified for stack object, " + "defaulting to blue.\n", pmProgname); + + _metrics.resolveColors(MetricList::perValue); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << "StackObj::finishedAdd: metrics: " << endl + << _metrics << endl; +#endif + + _stack = new StackMod(&_metrics, ViewObj::object(_shape), _height); + _root->addChild(_stack->root()); + + if (_text.length()) + _stack->setFillText((const char *)_text.toAscii()); + + BaseObj::add(_stack); + ViewObj::theNumModObjects++; + } + else + _length = 0; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + cerr << name() << "has length " << _length << endl; +#endif + + _width = baseWidth() + _length; + _depth = baseDepth() + _length; +} + +void +StackObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + BaseObj::setBaseSize(width(), depth()); + BaseObj::setTran(xTran + (width() / 2.0), + zTran + (depth() / 2.0), + setWidth, setDepth); +} + +QTextStream& +operator<<(QTextStream& os, StackObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +StackObj::display(QTextStream& os) const +{ + BaseObj::display(os); + os << ", length = " << _length << ": "; + if (_stack) + os << *_stack << endl; + else + os << "stack undefined!" << endl; +} diff --git a/src/pmview/stackobj.h b/src/pmview/stackobj.h new file mode 100644 index 0000000..76fed4a --- /dev/null +++ b/src/pmview/stackobj.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _STACKOBJ_H_ +#define _STACKOBJ_H_ + +#include "modobj.h" +#include "stackmod.h" + +class SoSeparator; + +class StackObj : public ModObj +{ +protected: + + int _width; + int _depth; + StackMod::Height _height; + ViewObj::Shape _shape; + StackMod *_stack; + QString _text; + +public: + + virtual ~StackObj(); + + StackObj(StackMod::Height height, + ViewObj::Shape shape, + bool baseFlag, + const DefaultObj &defaults, + int x, int z, + int cols = 1, int rows = 1, + BaseObj::Alignment align = BaseObj::center); + + virtual int width() const + { return _width; } + virtual int depth() const + { return _depth; } + StackMod::Height height() const + { return _height; } + + void setFillText(const char *str) + { _text = str; } + + virtual void finishedAdd(); + + virtual void setTran(float xTran, float zTran, int width, int depth); + + virtual const char* name() const + { return "Stack"; } + + virtual void display(QTextStream& os) const; + + friend QTextStream& operator<<(QTextStream& os, StackObj const& rhs); + + virtual void setBarHeight (int h) { _maxHeight = h; } + +private: + + StackObj(); + StackObj(StackObj const&); + StackObj const& operator=(StackObj const &); +}; + +#endif /* _STACKOBJ_H_ */ diff --git a/src/pmview/text.cpp b/src/pmview/text.cpp new file mode 100644 index 0000000..f4547b4 --- /dev/null +++ b/src/pmview/text.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/Qt/viewers/SoQtExaminerViewer.h> +#include <Inventor/actions/SoGetBoundingBoxAction.h> +#include <Inventor/nodes/SoPickStyle.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoRotation.h> +#include <Inventor/nodes/SoCube.h> +#include "main.h" +#include "text.h" + +#include <iostream> +using namespace std; + +const char *Text::theHeightStr = "gjpqy|_"; + +SoFont *Text::theSmallFont = (SoFont *)0; +SoFont *Text::theMediumFont = (SoFont *)0; +SoFont *Text::theLargeFont = (SoFont *)0; + +SbVec3f Text::theColor(1.0, 1.0, 1.0); +SoGetBoundingBoxAction *Text::theBoxAction = (SoGetBoundingBoxAction *)0; + +Text::~Text() +{ +} + +Text::Text(const QString &theString, + Direction theDir, + FontSize theFontSize, + bool rightJust) +: _width(0), + _depth(0), + _dir(theDir), + _fontSize(theFontSize), + _rightJustFlag(rightJust), + _root(0), + _translation(0) +{ + float x = 0.0; + float y = 0.0; + float z = 0.0; + int width = 0; + int height = 0; + SoRotation *rot1; + SoRotation *rot2; + SoText3 *theText; + + _root = new SoSeparator; + _translation = new SoTranslation; + theText = new SoText3; + + SoPickStyle *style = new SoPickStyle; + style->style = SoPickStyle::UNPICKABLE; + _root->addChild(style); + + if (theSmallFont == (SoFont *)0) { + char * font = getenv ("PMVIEW_FONT"); + + theSmallFont = new SoFont; + theMediumFont = new SoFont; + theLargeFont = new SoFont; + + if ( font != NULL ) { + theSmallFont->name.setValue(font); + theMediumFont->name.setValue(font); + theLargeFont->name.setValue(font); +#ifdef __sgi + } else { + // On Irix we know that Helvetica-Narrow is shipped as part + // of x_eoe.sw.Xfonts, so we're going to use it since it's + // an easier font to render compared to Times. + theSmallFont->name.setValue("Helvetica-Narrow"); + theMediumFont->name.setValue("Helvetica-Narrow"); + theLargeFont->name.setValue("Helvetica-Narrow"); +#endif + } + + theSmallFont->size.setValue(14); + theMediumFont->size.setValue(24); + theLargeFont->size.setValue(32); + theSmallFont->ref(); + theMediumFont->ref(); + theLargeFont->ref(); + theBoxAction = new SoGetBoundingBoxAction(pmview->viewer()->getViewportRegion()); + } + + switch(_fontSize) { + case small: + _root->addChild(theSmallFont->copy()); + break; + case medium: + _root->addChild(theMediumFont->copy()); + break; + case large: + _root->addChild(theLargeFont->copy()); + break; + default: +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Text::Text: Illegal size specified" << endl; +#endif + _fontSize = medium; + _root->addChild(theMediumFont->copy()); + } + + _translation->translation.setValue(0.0, 0.0, 0.0); + _root->addChild(_translation); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + SoSeparator *sep = new SoSeparator; + _root->addChild(sep); + SoBaseColor *col = new SoBaseColor; + col->rgb.setValue(1.0, 0.0, 0.0); + sep->addChild(col); + SoCube *cube = new SoCube; + cube->width.setValue(3); + cube->depth.setValue(3); + cube->height.setValue(20); + sep->addChild(cube); + } +#endif + + if (_dir != vertical) { + + rot1 = new SoRotation; + rot1->rotation.setValue(SbVec3f(1,0,0), -M_PI/2); + _root->addChild(rot1); + + switch(_dir) { + case left: + rot2 = new SoRotation; + rot2->rotation.setValue(SbVec3f(0,0,1), M_PI); + _root->addChild(rot2); + break; + case down: + rot2 = new SoRotation; + rot2->rotation.setValue(SbVec3f(0,0,1), -M_PI/2); + _root->addChild(rot2); + break; + case up: + rot2 = new SoRotation; + rot2->rotation.setValue(SbVec3f(0,0,1), M_PI/2); + _root->addChild(rot2); + break; + default: + break; + } + + if (((_dir == left || _dir == up) && !_rightJustFlag) || + ((_dir == right || _dir == down) && !_rightJustFlag)) { + theText->justification = SoText3::RIGHT; + } + + theText->parts = SoText3::FRONT; + theText->string.setValue((const char *)theString.toAscii()); + _root->addChild(theText); + + _root->ref(); + theBoxAction->apply(_root); + SbXfBox3f box = theBoxAction->getBoundingBox(); + box.getSize(x, y, z); + + if (x < 0.0 || y < 0.0 || z < 0.0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + cerr << "Text::Text: Bogus bounding box returned for \"" + << theString << "\": x = " << x << ", y = " << y + << ", z = " << z << endl; + } +#endif + x = 0.0; + y = 0.0; + z = 0.0; + } + + _width = (int)ceilf(x); + _depth = (int)ceilf(z); + + const char *hasLow = strpbrk((const char *)theString.toAscii(), theHeightStr); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + cerr << "Text::Text: " << theString << ": width = " + << _width << " height = " << _depth << " low = " + << ((hasLow != (char *)0) ? 1 : 0) << endl; + } +#endif + + switch(_dir) { + case left: + if (hasLow != (char *)0) { + _translation->translation.setValue(1, 1, _depth * 0.3); + _depth = (int)(_depth * 1.2); + } + else + _translation->translation.setValue(1, 1, 0); + _width += 2; + break; + case right: + if (hasLow != (char *)0) { + _translation->translation.setValue(1, 1, _depth * 0.85); + _depth = (int)(_depth * 1.2); + } + else + _translation->translation.setValue(1, 1, _depth); + _width += 2; + break; + case down: + if (hasLow != (char *)0) { + _translation->translation.setValue(_width * 0.3, 1, 1); + _width = (int)(_width * 1.2); + } + else + _translation->translation.setValue(0, 1, 1); + _depth += 2; + break; + case up: + if (hasLow != (char *)0) { + _translation->translation.setValue(_width * 0.85, 1, 1); + _width = (int)(_width * 1.2); + } + else + _translation->translation.setValue(_width, 1, 1); + _depth += 2; + break; + default: +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + cerr << "Text::Text: Illegal direction specified (" + << (int)_dir << ")" << endl; +#endif + break; + } + } + else { + char c[2] = { ' ', '\0' }; + SoSeparator *sep; + SoTranslation *tran; + SoRotation *rot; + width = 0; + height = 0; + + for (int i = 0; i < theString.length(); i++) { + c[0] = ((const char *)theString.toAscii())[i]; + sep = new SoSeparator; + tran = new SoTranslation; + rot = new SoRotation; + rot->rotation.setValue(SbVec3f(1,0,0), -M_PI/2); + theText->string.setValue(c); + theText->parts = SoText3::FRONT; + + _root->addChild(sep); + sep->addChild(rot); + sep->addChild(tran); + sep->addChild(theText); + + theBoxAction->apply(sep); + SbXfBox3f box = theBoxAction->getBoundingBox(); + box.getSize(x, y, z); + + width = (int)ceilf(x); + height = (int)ceilf(z); + +// TODO: This bounding box is wrong. + + if (width*2 > _width) + _width = width*2; + + _depth += (height + 2) * 2; + + tran->translation.setValue(0, -_depth, 1); + + if (i < theString.length() - 1) { + theText = new SoText3; + } + } + } +} + + +QTextStream& +operator<<(QTextStream& os, Text const& rhs) +{ + rhs.display(os); + return os; +} + +void +Text::display(QTextStream& os) const +{ + os << "Text: dir = "; + switch(_dir) { + case left: + os << "left"; + break; + case right: + os << "right"; + break; + case up: + os << "up"; + break; + case down: + os << "down"; + break; + default: + break; + } + os << ", font size = "; + switch(_fontSize) { + case small: + os << "small"; + break; + case medium: + os << "medium"; + break; + case large: + os << "large"; + break; + } +} diff --git a/src/pmview/text.h b/src/pmview/text.h new file mode 100644 index 0000000..f9a6854 --- /dev/null +++ b/src/pmview/text.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _TEXT_H_ +#define _TEXT_H_ + +#include <QtCore/QTextStream> +#include <Inventor/nodes/SoFont.h> +#include <Inventor/nodes/SoText3.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoBaseColor.h> + +class Text +{ + public: + + enum Direction { left, right, up, down, vertical }; + enum FontSize { small, medium, large }; + + private: + + int _width; + int _depth; + Direction _dir; + FontSize _fontSize; + bool _rightJustFlag; + + SoSeparator *_root; + SoTranslation *_translation; + + static const char *theHeightStr; + static SoFont *theSmallFont; + static SoFont *theMediumFont; + static SoFont *theLargeFont; + + static SbVec3f theColor; + static SoGetBoundingBoxAction *theBoxAction; + + public: + + ~Text(); + + Text(const QString &theText, + Direction theDir, + FontSize theFontSize, + bool rightJust = false); + + int width() const + { return _width; } + int depth() const + { return _depth; } + Direction dir() const + { return _dir; } + FontSize size() const + { return _fontSize; } + + SoSeparator* root() const + { return _root; } + + friend QTextStream& operator<<(QTextStream& os, Text const& rhs); + + void display(QTextStream& os) const; + + private: + + Text(); + Text(Text const &); + Text const& operator=(Text const&); +}; + +#endif /* _TEXT_H_ */ diff --git a/src/pmview/togglemod.cpp b/src/pmview/togglemod.cpp new file mode 100644 index 0000000..282bbe8 --- /dev/null +++ b/src/pmview/togglemod.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoSelection.h> +#include <Inventor/SoPath.h> +#include "main.h" +#include "togglemod.h" + +#include <iostream> +using namespace std; + +ToggleMod::~ToggleMod() +{ +} + +ToggleMod::ToggleMod(SoNode *obj, const char *label) +: Modulate(NULL), + _label(label) +{ + _root = new SoSeparator; + _root->addChild(obj); + + if (_label.length() == 0) + _label = "\n"; + + add(); +} + +void +ToggleMod::dump(QTextStream &os) const +{ + os << "ToggleMod: \"" << _label << "\" has " << _list.size() + << " objects" << endl; +} + +void +ToggleMod::selectAll() +{ + int i; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ToggleMod::selectAll: \"" << _label << '"' << endl; +#endif + + for (i = 0; i < _list.size(); i++) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ToggleMod::selectAll: Selecting [" << i << ']' + << endl; +#endif + + _list[i]->selectAll(); + } +} + +int +ToggleMod::select(SoPath *path) +{ + int i; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ToggleMod::select: \"" << _label << '"' << endl; +#endif + + theModList->selectAllOn(); + + for (i = 0; i < _list.size(); i++) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ToggleMod::select: Selecting [" << i << ']' << endl; +#endif + + _list[i]->selectAll(); + } + + theModList->selectAllOff(); + + theModList->selector()->deselect(path->getNodeFromTail(0)); + + return 0; +} + +int +ToggleMod::remove(SoPath *) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + cerr << "ToggleMod::remove: " << _label << endl; +#endif + return 0; +} + diff --git a/src/pmview/togglemod.h b/src/pmview/togglemod.h new file mode 100644 index 0000000..dafd65e --- /dev/null +++ b/src/pmview/togglemod.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _TOGGLEMOD_H_ +#define _TOGGLEMOD_H_ + +#include <Inventor/SbString.h> +#include "modulate.h" +#include "modlist.h" + +class SoSeparator; +class SoPath; +class Launch; +class Record; + +class ToggleMod : public Modulate +{ +private: + + ModulateList _list; + QString _label; + +public: + + virtual ~ToggleMod(); + + ToggleMod(SoNode *obj, const char *label); + + void addMod(Modulate *mod) + { _list.append(mod); } + + virtual void selectAll(); + virtual int select(SoPath *); + virtual int remove(SoPath *); + + virtual void selectInfo(SoPath *) + {} + virtual void removeInfo(SoPath *) + {} + + virtual void infoText(QString &str, bool) const + { str = _label; } + + virtual void refresh(bool) + {} + virtual void launch(Launch &, bool) const + {} + virtual void record(Record &) const + {} + + virtual void dump(QTextStream &) const; + void dumpState(QTextStream &os, Modulate::State state) const; + + friend QTextStream &operator<<(QTextStream &os, const ToggleMod &rhs); + +private: + + ToggleMod(); + ToggleMod(const ToggleMod &); + const ToggleMod &operator=(const ToggleMod &); + // Never defined +}; + +#endif /* _TOGGLEMOD_H_ */ diff --git a/src/pmview/viewobj.cpp b/src/pmview/viewobj.cpp new file mode 100644 index 0000000..f0e9b30 --- /dev/null +++ b/src/pmview/viewobj.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoCube.h> +#include <Inventor/nodes/SoCylinder.h> +#include "pmview.h" +#include "viewobj.h" + +#include <iostream> +using namespace std; + +int ViewObj::theNumModObjects = 0; + +ViewObj::~ViewObj() +{ +} + +ViewObj::ViewObj(int x, int z, int cols, int rows, Alignment align) + : _root(new SoSeparator) + , _tran(new SoTranslation) + , _objtype(VIEWOBJ) + , _col(x) + , _row(z) + , _cols(cols) + , _rows(rows) +{ + _tran->translation.setValue(0.0, 0.0, 0.0); + _root->addChild(_tran); + + switch(align) { + case north: + _xAlign = 0.5; + _zAlign = 0.0; + break; + case south: + _xAlign = 0.5; + _zAlign = 1.0; + break; + case east: + _xAlign = 1.0; + _zAlign = 0.5; + break; + case west: + _xAlign = 0.0; + _zAlign = 0.5; + break; + case northEast: + _xAlign = 1.0; + _zAlign = 0.0; + break; + case northWest: + _xAlign = 0.0; + _zAlign = 0.0; + break; + case southEast: + _xAlign = 1.0; + _zAlign = 1.0; + break; + case southWest: + _xAlign = 0.0; + _zAlign = 1.0; + break; + case center: + default: + _xAlign = 0.5; + _zAlign = 0.5; + break; + } +} + +SoNode * +ViewObj::object(Shape shape) +{ + static SoSeparator *cubeSep = NULL; + static SoSeparator *cylSep = NULL; + SoNode *obj; + + switch(shape) { + case cylinder: + if (cylSep == NULL) { + cylSep = new SoSeparator(); + SoTranslation *cylTran = new SoTranslation; + cylTran->translation.setValue(0.0, 0.5, 0.0); + cylSep->addChild(cylTran); + SoCylinder *cyl = new SoCylinder; + cyl->radius.setValue(0.5); + cyl->height.setValue(1.0); + cylSep->addChild(cyl); + } + obj = cylSep; + break; + + case cube: + default: + if (cubeSep == NULL) { + cubeSep = new SoSeparator(); + SoTranslation *cubeTran = new SoTranslation; + cubeTran->translation.setValue(0.0, 0.5, 0.0); + cubeSep->addChild(cubeTran); + SoCube *cube = new SoCube; + cube->height = cube->width = cube->depth = 1.0; + cubeSep->addChild(cube); + } + obj = cubeSep; + break; + } + + return obj; +} + +void +ViewObj::setTran(float xTran, float zTran, int setWidth, int setDepth) +{ + float x = xTran + ((setWidth - (float)width()) * _xAlign); + float z = zTran + ((setDepth - (float)depth()) * _zAlign); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + cerr << "ViewObj::setTran: " << name() << ":" << endl; + cerr << "x=" << x << ", xTran =" << xTran << ", setWidth=" + << setWidth << ", width=" << width() << ", xAlign=" << _xAlign + << endl; + cerr << "z=" << z << ", zTran =" << zTran << ", setDepth=" + << setDepth << ", depth=" << depth() << ", zAlign=" << _zAlign + << endl << endl; + } +#endif + + _tran->translation.setValue(x, 0.0, z); +} + +QTextStream& +operator<<(QTextStream& os, ViewObj const& rhs) +{ + rhs.display(os); + return os; +} + +void +ViewObj::display(QTextStream& os) const +{ + os << name() << ": size = " << width() << "x" << depth() << " (" + << _tran->translation.getValue()[0] << ',' + << _tran->translation.getValue()[1] << ',' + << _tran->translation.getValue()[2] << "), cols = " << _cols + << ", rows = " << _rows << ", alignment = " << _xAlign << ',' + << _zAlign; +} + +void +ViewObj::dumpShape(QTextStream& os, ViewObj::Shape shape) const +{ + if (shape == ViewObj::cube) + os << "cube"; + else + os << "cylinder"; +} diff --git a/src/pmview/viewobj.h b/src/pmview/viewobj.h new file mode 100644 index 0000000..f713c71 --- /dev/null +++ b/src/pmview/viewobj.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _VIEWOBJ_H_ +#define _VIEWOBJ_H_ + +#include <QtCore/QTextStream> + +class SoNode; +class SoSeparator; +class SoTranslation; +class Modulate; +class DefaultObj; + +class ViewObj +{ +public: + + enum Alignment { north, south, east, west, + northEast, northWest, southEast, southWest, + center }; + + enum Shape { cube, cylinder }; + + // Poor man substitution for RTTI until O32 compiler will either + // die or start supporting that stuff + enum ObjType {VIEWOBJ = 1, + BASEOBJ = 2, + LABELOBJ = 4, + MODOBJ = 8, + GRIDOBJ = 16, + PIPEOBJ = 32, + BAROBJ = 64, + STACKOBJ = 128, + LINK = 256, + XING = 512, + SCENEFILEOBJ = 1024 + }; + +protected: + + SoSeparator *_root; + SoTranslation *_tran; + + int _objtype; + int _col; + int _row; + int _cols; + int _rows; + int _maxHeight; + float _xAlign; + float _zAlign; + + static int theNumModObjects; + +public: + + virtual ~ViewObj(); + + ViewObj(int, int, int cols = 1, int rows = 1, + Alignment align = center); + + // The Scene Graph Root for this object + SoSeparator* root() + { return _root; } + + int objbits() const { return _objtype; } + int row() const { return _row; } + int col() const { return _col; } + + int cols() const + { return _cols; } + int rows() const + { return _rows; } + int height() const + { return _maxHeight; } + int &height() + { return _maxHeight; } + float xAlign() const + { return _xAlign; } + float zAlign() const + { return _zAlign; } + + // Set the coordinates (and the allocated size) from parent + virtual void setTran(float xTran, float zTran, int width, int depth); + + // Size (in object coordinates). Must be the correct value before + // the object is added to the parent. + virtual int width() const = 0; + virtual int depth() const = 0; + + static int numModObjects() + { return theNumModObjects; } + + // Return default object + static SoNode *object(Shape shape); + + virtual Modulate *modObj() + { return (Modulate *)0; } + + // Inform object parsing stuff is done + + virtual void finishedAdd() = 0; + + // Output + virtual void display(QTextStream& os) const; + + virtual const char* name() const = 0; + + friend QTextStream& operator<<(QTextStream& os, ViewObj const& rhs); + +protected: + + void dumpShape(QTextStream& os, ViewObj::Shape shape) const; + +private: + + ViewObj(); + ViewObj(ViewObj const &); + ViewObj const& operator=(ViewObj const &); + // Never defined +}; + +#endif /* _VIEWOBJ_H_ */ diff --git a/src/pmview/xing.cpp b/src/pmview/xing.cpp new file mode 100644 index 0000000..c7637e0 --- /dev/null +++ b/src/pmview/xing.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoSphere.h> +#include <Inventor/nodes/SoSeparator.h> +#include <Inventor/nodes/SoTranslation.h> +#include <Inventor/nodes/SoTransform.h> +#include <Inventor/nodes/SoRotation.h> +#include <Inventor/nodes/SoCylinder.h> +#include <Inventor/nodes/SoClipPlane.h> +#include <Inventor/nodes/SoBaseColor.h> + +#include "xing.h" +#include "defaultobj.h" + +Xing::Xing (const DefaultObj & dobj, int c, int r, + int colSpan, int rowSpan, + Alignment corners[4]) + : BaseObj (false, dobj, c, r, colSpan, rowSpan, center) +{ + int i; + _objtype |= XING; + + for (i = 0; i < 3; i++) { + _color[i] = dobj.baseColor(i); + } + + _width = colSpan * _cellWidth; + _depth = rowSpan * _cellDepth; + _cellHeight = dobj.baseHeight(); + _cellWidth = dobj.baseHeight(); + _cellDepth = dobj.baseHeight(); + + for ( i=0; i < 4; i++ ) { + _corner[i] = corners[i]; + } +} + +void +Xing::finishedAdd () +{ + int i; + float ts[12] = { + (float)(_cellWidth/2.0), (float)(_cellHeight/2.0), (float)(_cellDepth/2.0), + (float)(_width - _cellWidth), 0, 0, + 0, 0, (float)(_depth - _cellDepth), + (float)(_cellWidth - _width), 0, 0 + }; + + SoBaseColor * cl = new SoBaseColor; + cl->rgb.setValue (_color); + _root->addChild (cl); + + SoSeparator * scene = new SoSeparator; + + for (i=0; i < 4; i++ ) { + SoTranslation * t = new SoTranslation; + t->translation.setValue (ts+i*3); + scene->addChild(t); + + SoSphere * s = new SoSphere; + s->radius.setValue (_cellHeight*0.4); + scene->addChild (s); + + SoSeparator * csep = new SoSeparator; + + float ch; + + ctrans[i] = new SoTransform; + switch ( _corner[i] ) { + case south: + ch = _cellDepth/2.0; + ctrans[i]->translation.setValue (0, 0, _cellDepth/4.0); + ctrans[i]->rotation.setValue (SbVec3f(1, 0, 0), M_PI/2); + break; + + case east: + ch = _cellWidth/2.0; + ctrans[i]->translation.setValue (_cellWidth/4.0, 0, 0); + ctrans[i]->rotation.setValue (SbVec3f(0, 0, 1), M_PI/2); + break; + + case west: + ch = _cellWidth/2.0; + ctrans[i]->translation.setValue (_cellWidth/-4.0, 0, 0); + ctrans[i]->rotation.setValue (SbVec3f(0, 0, 1), M_PI/2); + break; + + case north: + ch = _cellDepth/2.0; + ctrans[i]->translation.setValue (0, 0, _cellDepth/-4.0); + ctrans[i]->rotation.setValue (SbVec3f(1, 0, 0), M_PI/2); + break; + + default: + ch = 0.0; + break; + } + csep->addChild (ctrans[i]); + + cyls[i] = new SoCylinder; + cyls[i]->radius.setValue (_cellHeight*0.4); + cyls[i]->height.setValue (ch); + csep->addChild (cyls[i]); + + scene->addChild (csep); + } + + float cilh = sqrt (_cellWidth*_cellWidth*(cols()-1)*(cols()-1) + + _cellDepth*_cellDepth*(rows()-1)*(rows()-1)); + + // We're in the bottom left corner now + SbVec3f axis(rows()-1, 0, cols()-1); + SoTransform * tr = new SoTransform; + tr->rotation.setValue (axis, M_PI/2); + tr->translation.setValue (((cols()-1)*_cellWidth)/2.0, 0, + ((rows()-1)*_cellDepth)/-2.0); + + scene->addChild (tr); + + SoCylinder * c1 = new SoCylinder; + c1->radius.setValue (_cellHeight*0.4); + c1->height.setValue (cilh); + scene->addChild (c1); + + axis[0] = cols() - 1; + axis[2] = -((float)(rows()-1)); + + // To draw a "broken" cylinder, define two clipping planes along the + // "main" cylinder and draw the crossing one twice, each time enabling + // a different plane. + for (i=-1; i < 2; i += 2 ) { + SoSeparator * sp = new SoSeparator; + + SbVec3f normal(i*((int)(rows()-1)), 0, i*((int)(cols()-1))); + SbPlane p (normal, _cellHeight/2.0); + + SoClipPlane * c = new SoClipPlane; + c->on.setValue (1); + c->plane = p; + + sp->addChild (c); + + SoRotation * r = new SoRotation; + r->rotation.setValue (axis, atanf (axis[0]/(rows()-1))*2.0); + sp->addChild (r); + sp->addChild (c1); + + scene->addChild (sp); + } + + _root->addChild (scene); +} + +void +Xing::setTran(float x, float z, int w, int d) +{ + float ch; + + for ( int i=0; i < 4; i++) { + switch ( _corner[i] ) { + case south: + ch = (d + _cellDepth - _depth)/2.0; + ctrans[i]->translation.setValue (0, 0, ch/2.0); + break; + + case east: + ch = (w + _cellWidth - _width)/2.0; + ctrans[i]->translation.setValue (ch/2.0, 0, 0); + break; + + case west: + ch = (w +_cellWidth - _width)/2.0; + ctrans[i]->translation.setValue (-ch/2.0, 0, 0); + break; + + case north: + ch = (d + _cellDepth - _depth)/2.0; + ctrans[i]->translation.setValue (0, 0, -ch/2.0); + break; + + default: + ch = 0.0; + break; + } + + cyls[i]->height.setValue (ch); + } + + BaseObj::setTran (x, z, w, d); +} diff --git a/src/pmview/xing.h b/src/pmview/xing.h new file mode 100644 index 0000000..b5920bd --- /dev/null +++ b/src/pmview/xing.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _XING_H_ +#define _XING_H_ + +#include "baseobj.h" + +class SoTransform; +class SoCylinder; + +class Xing : public BaseObj +{ +public: + Xing (const DefaultObj &, int, int, int, int, Alignment[4]); + + void finishedAdd (); + void setTran (float, float, int, int); + int width () const { return _width; } + int depth () const { return _depth; } + const char * name () const { return "Xing"; } + +private: + Xing(); + Xing(Xing const &); + Xing const & operator= (Xing const &); + + int _width; + int _depth; + int _cellHeight, _cellDepth, _cellWidth; + float _color[3]; + Alignment _corner[4]; + SoTransform * ctrans[4]; + SoCylinder * cyls[4]; +}; + +#endif /* _XING_H_ */ diff --git a/src/pmview/yscalemod.cpp b/src/pmview/yscalemod.cpp new file mode 100644 index 0000000..63359f5 --- /dev/null +++ b/src/pmview/yscalemod.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <Inventor/nodes/SoBaseColor.h> +#include <Inventor/nodes/SoScale.h> +#include <Inventor/nodes/SoSeparator.h> +#include "main.h" +#include "yscalemod.h" +#include "modlist.h" +#include "launch.h" + +YScaleMod::~YScaleMod() +{ +} + +YScaleMod::YScaleMod(const char *str, double scale, + const SbColor &color, SoNode *obj) +: ScaleMod(str, scale, color, obj, 0.0, 1.0, 0.0) +{ +} + +void +YScaleMod::dump(QTextStream &os) const +{ + os << "YScaleMod: "; + + if (status() < 0) + os << "Invalid metric"; + else { + os << "state = "; + dumpState(os, _state); + os << ", scale = " << _scale->scaleFactor.getValue()[1] << ": "; + _metrics->metric(0).dump(os, true); + } +} diff --git a/src/pmview/yscalemod.h b/src/pmview/yscalemod.h new file mode 100644 index 0000000..a0458f6 --- /dev/null +++ b/src/pmview/yscalemod.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2009 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _YSCALEMOD_H_ +#define _YSCALEMOD_H_ + +#include "scalemod.h" + +class SoBaseColor; +class SoScale; +class SoNode; +class Launch; + +class YScaleMod : public ScaleMod +{ +public: + + virtual ~YScaleMod(); + + YScaleMod(const char *metric, double scale, const SbColor &color, + SoNode *obj); + + virtual void dump(QTextStream &) const; + +private: + + YScaleMod(); + YScaleMod(const YScaleMod &); + const YScaleMod &operator=(const YScaleMod &); + // Never defined +}; + +#endif /* _YSCALEMOD_H_ */ |