diff --git a/client/about.ui b/client/about.ui
index 3953af0..6fb2711 100644
--- a/client/about.ui
+++ b/client/about.ui
@@ -104,7 +104,7 @@
-
- <a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a>
+ <a href="https://ostinato.org">ostinato.org</a><br><a href="https://twitter.com/ostinato">@ostinato</a>
Qt::RichText
@@ -155,7 +155,7 @@ Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/)
-
- <p>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 3 of the License, or (at your option) any later version.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p>
+ <p>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 3 of the License, or (at your option) any later version.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a></p>
Qt::RichText
diff --git a/client/devicegroupmodel.cpp b/client/devicegroupmodel.cpp
index 5b90765..0b20eff 100644
--- a/client/devicegroupmodel.cpp
+++ b/client/devicegroupmodel.cpp
@@ -118,7 +118,7 @@ QVariant DeviceGroupModel::data(const QModelIndex &index, int role) const
return v;
return QString("None");
case Qt::TextAlignmentRole:
- return Qt::AlignRight;
+ return static_cast(Qt::AlignRight|Qt::AlignVCenter);
default:
break;
}
@@ -129,7 +129,7 @@ QVariant DeviceGroupModel::data(const QModelIndex &index, int role) const
case Qt::DisplayRole:
return qMax(vlanCount(devGrp), 1)*devGrp->device_count();
case Qt::TextAlignmentRole:
- return Qt::AlignRight;
+ return static_cast(Qt::AlignRight|Qt::AlignVCenter);
default:
break;
}
diff --git a/client/deviceswidget.cpp b/client/deviceswidget.cpp
index 64bbbfe..1a4b303 100644
--- a/client/deviceswidget.cpp
+++ b/client/deviceswidget.cpp
@@ -32,7 +32,7 @@ DevicesWidget::DevicesWidget(QWidget *parent)
setupUi(this);
deviceGroupList->setVisible(deviceConfig->isChecked());
deviceList->setVisible(deviceInfo->isChecked());
- refresh->setVisible(deviceInfo->isChecked());
+ setDeviceInfoButtonsVisible(deviceInfo->isChecked());
deviceDetail->hide();
deviceGroupList->verticalHeader()->setDefaultSectionSize(
@@ -162,7 +162,7 @@ void DevicesWidget::updateDeviceViewActions()
void DevicesWidget::on_deviceInfo_toggled(bool checked)
{
- refresh->setVisible(checked);
+ setDeviceInfoButtonsVisible(checked);
deviceGroupList->setHidden(checked);
deviceList->setVisible(checked);
deviceDetail->hide();
@@ -236,6 +236,40 @@ void DevicesWidget::on_refresh_clicked()
.getDeviceInfo(portGroups_->port(currentPortIndex_).id());
}
+void DevicesWidget::on_resolveNeighbors_clicked()
+{
+ if (!portGroups_)
+ return;
+
+ Q_ASSERT(portGroups_->isPort(currentPortIndex_));
+ QModelIndex curPortGroup = portGroups_->getPortModel()
+ ->parent(currentPortIndex_);
+ Q_ASSERT(curPortGroup.isValid());
+ Q_ASSERT(portGroups_->isPortGroup(curPortGroup));
+
+ deviceDetail->hide();
+ QList portList({portGroups_->port(currentPortIndex_).id()});
+ portGroups_->portGroup(curPortGroup).resolveDeviceNeighbors(&portList);
+ portGroups_->portGroup(curPortGroup).getDeviceInfo(portList.at(0));
+}
+
+void DevicesWidget::on_clearNeighbors_clicked()
+{
+ if (!portGroups_)
+ return;
+
+ Q_ASSERT(portGroups_->isPort(currentPortIndex_));
+ QModelIndex curPortGroup = portGroups_->getPortModel()
+ ->parent(currentPortIndex_);
+ Q_ASSERT(curPortGroup.isValid());
+ Q_ASSERT(portGroups_->isPortGroup(curPortGroup));
+
+ deviceDetail->hide();
+ QList portList({portGroups_->port(currentPortIndex_).id()});
+ portGroups_->portGroup(curPortGroup).clearDeviceNeighbors(&portList);
+ portGroups_->portGroup(curPortGroup).getDeviceInfo(portList.at(0));
+}
+
void DevicesWidget::when_deviceList_currentChanged(const QModelIndex &index)
{
if (!index.isValid() || !portGroups_)
@@ -247,3 +281,11 @@ void DevicesWidget::when_deviceList_currentChanged(const QModelIndex &index)
deviceDetail->setModel(detailModel);
deviceDetail->setVisible(detailModel != NULL);
}
+
+void DevicesWidget::setDeviceInfoButtonsVisible(bool show)
+{
+ refresh->setVisible(show);
+ arpNdpLabel->setVisible(show);
+ resolveNeighbors->setVisible(show);
+ clearNeighbors->setVisible(show);
+}
diff --git a/client/deviceswidget.h b/client/deviceswidget.h
index d5ef524..d1eb38c 100644
--- a/client/deviceswidget.h
+++ b/client/deviceswidget.h
@@ -49,10 +49,14 @@ private slots:
void on_deviceGroupList_activated(const QModelIndex &index);
void on_refresh_clicked();
+ void on_resolveNeighbors_clicked();
+ void on_clearNeighbors_clicked();
void when_deviceList_currentChanged(const QModelIndex &index);
private:
+ void setDeviceInfoButtonsVisible(bool show);
+
PortGroupList *portGroups_;
QModelIndex currentPortIndex_;
};
diff --git a/client/deviceswidget.ui b/client/deviceswidget.ui
index 2e09917..ec76836 100644
--- a/client/deviceswidget.ui
+++ b/client/deviceswidget.ui
@@ -36,19 +36,6 @@
- -
-
-
- Qt::Horizontal
-
-
-
- 131
- 23
-
-
-
-
-
@@ -66,6 +53,60 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 131
+ 23
+
+
+
+
+ -
+
+
+ ARP/ND
+
+
+
+ -
+
+
+ Resolve Neighbors
+
+
+ Resolve Device Neighbors on selected port(s)
+
+
+
+
+
+
+ :/icons/neighbor_resolve.png:/icons/neighbor_resolve.png
+
+
+
+ -
+
+
+ Clear Neighbors
+
+
+ Clear Device Neighbors on selected port(s)
+
+
+
+
+
+
+ :/icons/neighbor_clear.png:/icons/neighbor_clear.png
+
+
+
-
diff --git a/client/icononlydelegate.h b/client/icononlydelegate.h
new file mode 100644
index 0000000..ef7369d
--- /dev/null
+++ b/client/icononlydelegate.h
@@ -0,0 +1,40 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#ifndef _ICON_ONLY_DELEGATE
+#define _ICON_ONLY_DELEGATE
+
+#include
+
+class IconOnlyDelegate : public QStyledItemDelegate
+{
+ using QStyledItemDelegate::QStyledItemDelegate;
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+ {
+ QStyleOptionViewItem opt = option;
+ opt.decorationPosition = QStyleOptionViewItem::Top;
+ opt.features &= ~QStyleOptionViewItem::HasDisplay;
+ QStyledItemDelegate::paint(painter, opt, index);
+ }
+};
+
+#endif
+
diff --git a/client/icons/deco_exclusive.png b/client/icons/deco_exclusive.png
deleted file mode 100644
index 911da3f..0000000
Binary files a/client/icons/deco_exclusive.png and /dev/null differ
diff --git a/client/icons/donate.png b/client/icons/donate.png
new file mode 100644
index 0000000..0ca9074
Binary files /dev/null and b/client/icons/donate.png differ
diff --git a/client/icons/frag_capture.png b/client/icons/frag_capture.png
new file mode 100644
index 0000000..2702f43
Binary files /dev/null and b/client/icons/frag_capture.png differ
diff --git a/client/icons/frag_exclusive.png b/client/icons/frag_exclusive.png
new file mode 100644
index 0000000..89e1aea
Binary files /dev/null and b/client/icons/frag_exclusive.png differ
diff --git a/client/icons/frag_link_down.png b/client/icons/frag_link_down.png
new file mode 100644
index 0000000..897ac8a
Binary files /dev/null and b/client/icons/frag_link_down.png differ
diff --git a/client/icons/frag_link_unknown.png b/client/icons/frag_link_unknown.png
new file mode 100644
index 0000000..3272798
Binary files /dev/null and b/client/icons/frag_link_unknown.png differ
diff --git a/client/icons/frag_link_up.png b/client/icons/frag_link_up.png
new file mode 100644
index 0000000..552cd58
Binary files /dev/null and b/client/icons/frag_link_up.png differ
diff --git a/client/icons/frag_transmit.png b/client/icons/frag_transmit.png
new file mode 100644
index 0000000..03132ed
Binary files /dev/null and b/client/icons/frag_transmit.png differ
diff --git a/client/icons/sound_mute.png b/client/icons/sound_mute.png
index b652d2a..e3399b0 100644
Binary files a/client/icons/sound_mute.png and b/client/icons/sound_mute.png differ
diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png
index b497ebd..6f81a86 100644
Binary files a/client/icons/sound_none.png and b/client/icons/sound_none.png differ
diff --git a/client/icons/transmit_on.png b/client/icons/transmit_on.png
new file mode 100644
index 0000000..f54bf73
Binary files /dev/null and b/client/icons/transmit_on.png differ
diff --git a/client/jumpurl.h b/client/jumpurl.h
index d91a3be..ddc9add 100644
--- a/client/jumpurl.h
+++ b/client/jumpurl.h
@@ -28,7 +28,7 @@ inline QString jumpUrl(
QString medium="hint",
QString name="help")
{
- return QString("http://jump.ostinato.org/" + keyword + "?"
+ return QString("https://jump.ostinato.org/" + keyword + "?"
+ "utm_source=" + source + "&"
+ "utm_medium=" + medium + "&"
+ "utm_campaign=" + name);
diff --git a/client/log.h b/client/log.h
new file mode 100644
index 0000000..b911dfc
--- /dev/null
+++ b/client/log.h
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#ifndef _LOG_H
+#define _LOG_H
+
+#include "logsmodel.h"
+
+extern LogsModel *appLogs;
+
+inline static void logError(QString port, QString message)
+{
+ appLogs->log(LogsModel::kError, port, message);
+}
+
+inline static void logError(quint32 portGroupId, quint32 portId,QString message)
+{
+ logError(QString("%1-%2").arg(portGroupId).arg(portId), message);
+}
+
+inline static void logError(quint32 portGroupId, QString message)
+{
+ logError(QString("%1-*").arg(portGroupId), message);
+}
+
+inline static void logError(QString message)
+{
+ logError(QString("--"), message);
+}
+
+inline static void logWarn(QString port, QString message)
+{
+ appLogs->log(LogsModel::kWarning, port, message);
+}
+
+inline static void logWarn(quint32 portGroupId, quint32 portId,QString message)
+{
+ logWarn(QString("%1-%2").arg(portGroupId).arg(portId), message);
+}
+
+inline static void logWarn(quint32 portGroupId, QString message)
+{
+ logWarn(QString("%1-*").arg(portGroupId), message);
+}
+
+inline static void logWarn(QString message)
+{
+ logWarn(QString("--"), message);
+}
+
+inline static void logInfo(QString port, QString message)
+{
+ appLogs->log(LogsModel::kInfo, port, message);
+}
+
+inline static void logInfo(quint32 portGroupId, quint32 portId,QString message)
+{
+ logInfo(QString("%1-%2").arg(portGroupId).arg(portId), message);
+}
+
+inline static void logInfo(quint32 portGroupId, QString message)
+{
+ logInfo(QString("%1-*").arg(portGroupId), message);
+}
+
+inline static void logInfo(QString message)
+{
+ logInfo(QString("--"), message);
+}
+#endif
+
diff --git a/client/logsmodel.cpp b/client/logsmodel.cpp
new file mode 100644
index 0000000..e749db9
--- /dev/null
+++ b/client/logsmodel.cpp
@@ -0,0 +1,145 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#include "logsmodel.h"
+
+#include
+
+// XXX: Keep the enum in sync with it's string
+enum {
+ kTimeStamp,
+ kLogLevel,
+ kPort,
+ kMessage,
+ kFieldCount
+};
+static QStringList columnTitles = QStringList()
+ << "Timestamp"
+ << "Level"
+ << "Port"
+ << "Message";
+
+static QStringList levelTitles = QStringList()
+ << "Info"
+ << "Warning"
+ << "Error";
+
+LogsModel::LogsModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+
+int LogsModel::rowCount(const QModelIndex &/*parent*/) const
+{
+ return logs_.size();
+}
+
+int LogsModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return kFieldCount;
+}
+
+QVariant LogsModel::headerData(
+ int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ switch (orientation) {
+ case Qt::Horizontal: // Column Header
+ return columnTitles.at(section);
+ case Qt::Vertical: // Row Header
+ return QVariant();
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+QVariant LogsModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || (index.row() >= logs_.size()))
+ return QVariant();
+
+ if (role == Qt::ForegroundRole) {
+ switch(logs_.at(index.row()).logLevel) {
+ case kError:
+ return QBrush(QColor("darkred"));
+ case kWarning:
+ return QBrush(QColor("orangered"));
+ case kInfo:
+ default:
+ return QVariant();
+ }
+ }
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ switch (index.column()) {
+ case kTimeStamp:
+ return logs_.at(index.row()).timeStamp.toString("hh:mm:ss.zzz");
+ case kLogLevel:
+ return levelTitles.at(logs_.at(index.row()).logLevel);
+ case kPort:
+ return logs_.at(index.row()).port;
+ case kMessage:
+ return logs_.at(index.row()).message;
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+// --------------------------------------------- //
+// Slots
+// --------------------------------------------- //
+void LogsModel::clear()
+{
+ beginResetModel();
+ logs_.clear();
+ endResetModel();
+}
+
+void LogsModel::setLogLevel(int level)
+{
+ currentLevel_ = static_cast(level % kLevelCount);
+ log(currentLevel_, QString("--"),
+ QString("Log level changed to %1 or higher")
+ .arg(levelTitles.at(currentLevel_)));
+}
+
+void LogsModel::log(int logLevel,QString port, QString message)
+{
+ if (logLevel < currentLevel_)
+ return;
+
+ // TODO: discard logs older than some threshold
+
+ //qDebug("adding log %u %s", logs_.size(), qPrintable(message));
+ beginInsertRows(QModelIndex(), logs_.size(), logs_.size());
+ Log l;
+ logs_.append(l);
+ logs_.last().timeStamp = QTime::currentTime();
+ logs_.last().logLevel = logLevel;
+ logs_.last().port = port;
+ logs_.last().message = message;
+ endInsertRows();
+}
diff --git a/client/logsmodel.h b/client/logsmodel.h
new file mode 100644
index 0000000..a7edb17
--- /dev/null
+++ b/client/logsmodel.h
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#ifndef _LOGS_MODEL_H
+#define _LOGS_MODEL_H
+
+#include
+#include
+
+class LogsModel: public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ enum LogLevel { // FIXME: use enum class?
+ kInfo,
+ kWarning,
+ kError,
+ kLevelCount
+ };
+
+public:
+ LogsModel(QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent=QModelIndex()) const;
+ int columnCount(const QModelIndex &parent=QModelIndex()) const;
+
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+public slots:
+ void clear();
+ void setLogLevel(int level);
+ void log(int logLevel,QString port, QString message);
+
+private:
+ struct Log {
+ QTime timeStamp;
+ int logLevel;
+ QString port;
+ QString message;
+ };
+ QVector logs_;
+ LogLevel currentLevel_{kInfo};
+};
+#endif
+
diff --git a/client/logswindow.cpp b/client/logswindow.cpp
new file mode 100644
index 0000000..f9cfd04
--- /dev/null
+++ b/client/logswindow.cpp
@@ -0,0 +1,114 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#include "logswindow.h"
+
+#include "logsmodel.h"
+#include "modeltest.h"
+
+#include
+#include
+
+LogsWindow::LogsWindow(LogsModel *model, QWidget *parent)
+ : QWidget(parent)
+{
+ setupUi(this);
+ logs->setModel(model);
+ autoScroll->setChecked(true);
+
+ logs->verticalHeader()->setHighlightSections(false);
+ logs->verticalHeader()->setDefaultSectionSize(
+ logs->verticalHeader()->minimumSectionSize());
+ logs->setShowGrid(false);
+ logs->setAlternatingRowColors(true);
+ logs->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
+
+ parentDock_ = qobject_cast(parent);
+ windowTitle_ = parentDock_->windowTitle();
+
+ connect(level, SIGNAL(currentIndexChanged(int)),
+ model, SLOT(setLogLevel(int)));
+ connect(clear, SIGNAL(clicked()), model, SLOT(clear()));
+
+ connect(parentDock_, SIGNAL(visibilityChanged(bool)),
+ SLOT(when_visibilityChanged(bool)));
+ connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
+ SLOT(when_rowsInserted(const QModelIndex&, int, int)));
+
+#if defined(QT_NO_DEBUG) || QT_VERSION < 0x050700
+ logsModelTest_ = nullptr;
+#else
+ logsModelTest_ = new ModelTest(model);
+#endif
+}
+
+LogsWindow::~LogsWindow()
+{
+ delete logsModelTest_;
+}
+
+void LogsWindow::when_visibilityChanged(bool visible)
+{
+ if (visible) {
+ parentDock_->setWindowTitle(windowTitle_);
+ annotation_.clear();
+ }
+
+ isVisible_ = visible;
+ qDebug("isVisible = %u", isVisible_);
+}
+
+void LogsWindow::when_rowsInserted(const QModelIndex &parent,
+ int first, int last)
+{
+ if (isVisible_)
+ return;
+
+ if (annotation_.contains("Error"))
+ return;
+
+ for (int i = first; i <= last; i++) {
+ // FIXME: use a user-role instead, so we don't need to know column and
+ // have to compare strings?
+ QString level = logs->model()->data(logs->model()->index(i, 1, parent))
+ .toString();
+ if (level == "Error") {
+ annotation_ = QString(" - Error(s)");
+ break; // Highest level - no need to look further
+ }
+ else if (level == "Warning") {
+ annotation_ = QString(" - Warning(s)");
+ }
+ }
+ parentDock_->setWindowTitle(windowTitle_+annotation_);
+}
+
+void LogsWindow::on_autoScroll_toggled(bool checked)
+{
+ if (checked) {
+ connect(logs->model(),
+ SIGNAL(rowsInserted(const QModelIndex&, int, int)),
+ logs, SLOT(scrollToBottom()));
+ }
+ else {
+ disconnect(logs->model(),
+ SIGNAL(rowsInserted(const QModelIndex&, int, int)),
+ logs, SLOT(scrollToBottom()));
+ }
+}
diff --git a/client/logswindow.h b/client/logswindow.h
new file mode 100644
index 0000000..ee3d59f
--- /dev/null
+++ b/client/logswindow.h
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#ifndef _LOGS_WINDOW_H
+#define _LOGS_WINDOW_H
+
+#include "ui_logswindow.h"
+
+class LogsModel;
+class QDockWidget;
+class QShowEvent;
+
+class LogsWindow: public QWidget, private Ui::LogsWindow
+{
+ Q_OBJECT
+public:
+ LogsWindow(LogsModel *model, QWidget *parent = 0);
+ ~LogsWindow();
+
+private slots:
+ void when_visibilityChanged(bool visible);
+ void when_rowsInserted(const QModelIndex &parent, int first, int last);
+ void on_autoScroll_toggled(bool checked);
+
+private:
+ QDockWidget *parentDock_;
+ QString windowTitle_;
+ QString annotation_;
+ bool isVisible_{false};
+ QObject *logsModelTest_;
+};
+
+#endif
+
diff --git a/client/logswindow.ui b/client/logswindow.ui
new file mode 100644
index 0000000..cabff41
--- /dev/null
+++ b/client/logswindow.ui
@@ -0,0 +1,137 @@
+
+
+ LogsWindow
+
+
+
+ 0
+ 0
+ 693
+ 300
+
+
+
+ Form
+
+
+
-
+
+
+ QFrame::Panel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Level
+
+
+ level
+
+
+
+ -
+
+
+ Log Level
+
+
+ Select the desired logging level
+
+
-
+
+ Info
+
+
+ -
+
+ Warning
+
+
+ -
+
+ Error
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Auto Scroll
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 429
+ 20
+
+
+
+
+ -
+
+
+ Clear Logs
+
+
+ Clear Logs
+
+
+ Clear
+
+
+
+ :/icons/portstats_clear_all.png:/icons/portstats_clear_all.png
+
+
+
+
+
+
+ -
+
+
+ Ostinato application logs are displayed here
+
+
+ QAbstractItemView::SelectRows
+
+
+
+
+
+
+
+ XTableView
+ QTableView
+
+
+
+
+ level
+ autoScroll
+ clear
+ logs
+
+
+
+
+
+
diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp
index 8138657..da375e0 100644
--- a/client/mainwindow.cpp
+++ b/client/mainwindow.cpp
@@ -24,6 +24,8 @@ along with this program. If not, see
#endif
#include "jumpurl.h"
+#include "logsmodel.h"
+#include "logswindow.h"
#include "params.h"
#include "portgrouplist.h"
#include "portstatswindow.h"
@@ -56,6 +58,7 @@ extern const char* version;
extern const char* revision;
PortGroupList *pgl;
+LogsModel *appLogs;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow (parent)
@@ -89,24 +92,39 @@ MainWindow::MainWindow(QWidget *parent)
localServer_ = NULL;
pgl = new PortGroupList;
+ appLogs = new LogsModel(this);
portsWindow = new PortsWindow(pgl, this);
statsWindow = new PortStatsWindow(pgl, this);
+
portsDock = new QDockWidget(tr("Ports and Streams"), this);
portsDock->setObjectName("portsDock");
portsDock->setFeatures(
portsDock->features() & ~QDockWidget::DockWidgetClosable);
+
statsDock = new QDockWidget(tr("Port Statistics"), this);
statsDock->setObjectName("statsDock");
statsDock->setFeatures(
statsDock->features() & ~QDockWidget::DockWidgetClosable);
+ logsDock_ = new QDockWidget(tr("Logs"), this);
+ logsDock_->setObjectName("logsDock");
+ logsDock_->setFeatures(
+ logsDock_->features() & ~QDockWidget::DockWidgetClosable);
+ logsWindow_ = new LogsWindow(appLogs, logsDock_);
+
setupUi(this);
menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions());
statsDock->setWidget(statsWindow);
addDockWidget(Qt::BottomDockWidgetArea, statsDock);
+ logsDock_->setWidget(logsWindow_);
+ addDockWidget(Qt::BottomDockWidgetArea, logsDock_);
+ tabifyDockWidget(statsDock, logsDock_);
+ statsDock->show();
+ statsDock->raise();
+
portsDock->setWidget(portsWindow);
addDockWidget(Qt::TopDockWidgetArea, portsDock);
@@ -334,6 +352,11 @@ void MainWindow::on_actionHelpOnline_triggered()
QDesktopServices::openUrl(QUrl(jumpUrl("help", "app", "menu")));
}
+void MainWindow::on_actionDonate_triggered()
+{
+ QDesktopServices::openUrl(QUrl(jumpUrl("donate", "app", "menu")));
+}
+
void MainWindow::on_actionHelpAbout_triggered()
{
QDialog *aboutDialog = new QDialog;
diff --git a/client/mainwindow.h b/client/mainwindow.h
index 8f1f22b..c8a47f0 100644
--- a/client/mainwindow.h
+++ b/client/mainwindow.h
@@ -24,6 +24,7 @@ along with this program. If not, see
#include
#include
+class LogsWindow;
class PortsWindow;
class PortStatsWindow;
@@ -41,8 +42,10 @@ private:
QProcess *localServer_;
PortsWindow *portsWindow;
PortStatsWindow *statsWindow;
+ LogsWindow *logsWindow_;
QDockWidget *portsDock;
QDockWidget *statsDock;
+ QDockWidget *logsDock_;
QRect defaultGeometry_;
QByteArray defaultLayout_;
@@ -57,6 +60,7 @@ public slots:
void on_actionPreferences_triggered();
void on_actionViewRestoreDefaults_triggered();
void on_actionHelpOnline_triggered();
+ void on_actionDonate_triggered();
void on_actionHelpAbout_triggered();
private slots:
diff --git a/client/mainwindow.ui b/client/mainwindow.ui
index 141c193..f6c195f 100644
--- a/client/mainwindow.ui
+++ b/client/mainwindow.ui
@@ -1,3 +1,4 @@
+
MainWindow
@@ -33,6 +34,8 @@
+
+
@@ -111,6 +114,14 @@
Help (Online)
+
+
+ :/icons/donate.png
+
+
+ Donate
+
+
diff --git a/client/ostinato.pro b/client/ostinato.pro
index b86d5a8..77aaa44 100644
--- a/client/ostinato.pro
+++ b/client/ostinato.pro
@@ -41,6 +41,8 @@ HEADERS += \
deviceswidget.h \
dumpview.h \
hexlineedit.h \
+ logsmodel.h \
+ logswindow.h \
mainwindow.h \
ndpstatusmodel.h \
packetmodel.h \
@@ -68,6 +70,7 @@ FORMS += \
about.ui \
devicegroupdialog.ui \
deviceswidget.ui \
+ logswindow.ui \
mainwindow.ui \
portconfigdialog.ui \
portstatsfilter.ui \
@@ -87,6 +90,8 @@ SOURCES += \
dumpview.cpp \
stream.cpp \
hexlineedit.cpp \
+ logsmodel.cpp \
+ logswindow.cpp \
main.cpp \
mainwindow.cpp \
ndpstatusmodel.cpp \
diff --git a/client/ostinato.qrc b/client/ostinato.qrc
index b0d57b7..2e9b98e 100644
--- a/client/ostinato.qrc
+++ b/client/ostinato.qrc
@@ -1,48 +1,55 @@
-
- icons/about.png
- icons/add.png
- icons/arrow_down.png
- icons/arrow_left.png
- icons/arrow_right.png
- icons/arrow_up.png
- icons/bullet_error.png
- icons/bullet_green.png
- icons/bullet_orange.png
- icons/bullet_red.png
- icons/bullet_white.png
- icons/bullet_yellow.png
- icons/control_play.png
- icons/control_stop.png
- icons/deco_exclusive.png
- icons/delete.png
- icons/devicegroup_add.png
- icons/devicegroup_delete.png
- icons/devicegroup_edit.png
- icons/exit.png
- icons/gaps.png
- icons/help.png
- icons/logo.png
- icons/magnifier.png
- icons/name.png
- icons/neighbor_clear.png
- icons/neighbor_resolve.png
- icons/portgroup_add.png
- icons/portgroup_connect.png
- icons/portgroup_delete.png
- icons/portgroup_disconnect.png
- icons/portstats_clear.png
- icons/portstats_clear_all.png
- icons/portstats_filter.png
- icons/preferences.png
- icons/qt.png
- icons/refresh.png
- icons/sound_mute.png
- icons/sound_none.png
- icons/stream_add.png
- icons/stream_delete.png
- icons/stream_duplicate.png
- icons/stream_edit.png
- icons/stream_stats.png
-
+
+ icons/about.png
+ icons/add.png
+ icons/arrow_down.png
+ icons/arrow_left.png
+ icons/arrow_right.png
+ icons/arrow_up.png
+ icons/bullet_error.png
+ icons/bullet_green.png
+ icons/bullet_orange.png
+ icons/bullet_red.png
+ icons/bullet_white.png
+ icons/bullet_yellow.png
+ icons/control_play.png
+ icons/control_stop.png
+ icons/delete.png
+ icons/devicegroup_add.png
+ icons/devicegroup_delete.png
+ icons/devicegroup_edit.png
+ icons/donate.png
+ icons/exit.png
+ icons/frag_capture.png
+ icons/frag_exclusive.png
+ icons/frag_link_down.png
+ icons/frag_link_unknown.png
+ icons/frag_link_up.png
+ icons/frag_transmit.png
+ icons/gaps.png
+ icons/help.png
+ icons/logo.png
+ icons/magnifier.png
+ icons/name.png
+ icons/neighbor_clear.png
+ icons/neighbor_resolve.png
+ icons/portgroup_add.png
+ icons/portgroup_connect.png
+ icons/portgroup_delete.png
+ icons/portgroup_disconnect.png
+ icons/portstats_clear.png
+ icons/portstats_clear_all.png
+ icons/portstats_filter.png
+ icons/preferences.png
+ icons/qt.png
+ icons/refresh.png
+ icons/sound_mute.png
+ icons/sound_none.png
+ icons/stream_add.png
+ icons/stream_delete.png
+ icons/stream_duplicate.png
+ icons/stream_edit.png
+ icons/stream_stats.png
+ icons/transmit_on.png
+
diff --git a/client/port.cpp b/client/port.cpp
index e9da599..c7154e7 100644
--- a/client/port.cpp
+++ b/client/port.cpp
@@ -531,7 +531,9 @@ void Port::updateStats(OstProto::PortStats *portStats)
oldState = stats.state();
stats.MergeFrom(*portStats);
- if (oldState.link_state() != stats.state().link_state())
+ if ((oldState.link_state() != stats.state().link_state())
+ || (oldState.is_transmit_on() != stats.state().is_transmit_on())
+ || (oldState.is_capture_on() != stats.state().is_capture_on()))
{
qDebug("portstate changed");
emit portDataChanged(mPortGroupId, mPortId);
diff --git a/client/port.h b/client/port.h
index d794480..9a7d7e6 100644
--- a/client/port.h
+++ b/client/port.h
@@ -130,6 +130,10 @@ public:
}
OstProto::LinkState linkState()
{ return stats.state().link_state(); }
+ bool isTransmitting()
+ { return stats.state().is_transmit_on(); }
+ bool isCapturing()
+ { return stats.state().is_capture_on(); }
OstProto::PortStats getStats() { return stats; }
QTemporaryFile* getCaptureFile()
diff --git a/client/portgroup.cpp b/client/portgroup.cpp
index 4c7180f..b8df277 100644
--- a/client/portgroup.cpp
+++ b/client/portgroup.cpp
@@ -20,6 +20,7 @@ along with this program. If not, see
#include "portgroup.h"
#include "jumpurl.h"
+#include "log.h"
#include "settings.h"
#include "emulproto.pb.h"
@@ -166,6 +167,7 @@ void PortGroup::on_rpcChannel_connected()
new OstProto::VersionCompatibility;
qDebug("connected\n");
+ logInfo(id(), "PortGroup connected");
emit portGroupDataChanged(mPortGroupId);
reconnectAfter = kMinReconnectWaitTime;
@@ -193,12 +195,16 @@ void PortGroup::processVersionCompatibility(PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), QString("checkVersion RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _error_exit;
}
if (verCompat->result() == OstProto::VersionCompatibility::kIncompatible) {
qWarning("incompatible version %s (%s)", version,
qPrintable(QString::fromStdString(verCompat->notes())));
+ logError(id(), QString("checkVersion failed: %1")
+ .arg(QString::fromStdString(verCompat->notes())));
compat = kIncompatible;
emit portGroupDataChanged(mPortGroupId);
@@ -236,6 +242,7 @@ _error_exit:
void PortGroup::on_rpcChannel_disconnected()
{
qDebug("disconnected\n");
+ logError(id(), "PortGroup disconnected");
emit portListAboutToBeChanged(mPortGroupId);
while (!mPorts.isEmpty())
@@ -250,6 +257,8 @@ void PortGroup::on_rpcChannel_disconnected()
if (reconnect)
{
qDebug("starting reconnect timer for %d ms ...", reconnectAfter);
+ logInfo(id(), QString("Reconnect attempt after %1s")
+ .arg(double(reconnectAfter)/1000.0));
reconnectTimer->start(reconnectAfter);
}
}
@@ -266,6 +275,8 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError)
if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect)
{
qDebug("starting reconnect timer for %d ms...", reconnectAfter);
+ logInfo(id(), QString("Reconnect attempt after %1s")
+ .arg(double(reconnectAfter)/1000.0));
reconnectTimer->start(reconnectAfter);
}
}
@@ -296,6 +307,10 @@ void PortGroup::on_rpcChannel_notification(int notifType,
qWarning("notif(portConfigChanged) has an empty port_id_list");
return;
}
+ for(int i=0; i < notif->port_id_list().port_id_size(); i++) {
+ logInfo(id(), notif->port_id_list().port_id(i).id(),
+ QString("Port configuration changed notification"));
+ }
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
OstProto::PortConfigList *portConfigList =
@@ -318,6 +333,7 @@ void PortGroup::when_portListChanged(quint32 /*portGroupId*/)
{
if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0)
{
+ logError(id(), QString("No ports in portlist"));
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Warning);
msgBox.setTextFormat(Qt::RichText);
@@ -348,6 +364,8 @@ void PortGroup::processPortIdList(PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), QString("getPortIdList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _error_exit;
}
@@ -403,6 +421,8 @@ void PortGroup::processPortConfigList(PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), QString("getPortConfig RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _error_exit;
}
@@ -453,6 +473,9 @@ void PortGroup::processPortConfigList(PbRpcController *controller)
if (!port->userName().isEmpty() // rsvd?
&& port->userName() != myself) // by someone else?
{
+ logWarn(id(), j, QString("Port is reserved by %1. "
+ "Skipping reconfiguration")
+ .arg(port->userName()));
QString warning =
QString("%1 - %2: %3 is reserved by %4.\n\n"
"Port will not be reconfigured.")
@@ -460,8 +483,8 @@ void PortGroup::processPortConfigList(PbRpcController *controller)
.arg(j)
.arg(port->userAlias())
.arg(port->userName());
- QMessageBox::warning(NULL, tr("Open Session"), warning);
qWarning("%s", qPrintable(warning));
+ QMessageBox::warning(NULL, tr("Open Session"), warning);
continue;
}
atConnectPortConfig_[j] = pc;
@@ -509,6 +532,8 @@ void PortGroup::when_configApply(int portIndex)
bool refreshReqd = false;
qDebug("applying 'deleted deviceGroups' ...");
+ logInfo(id(), mPorts[portIndex]->id(),
+ QString("Deleting old DeviceGroups"));
deviceGroupIdList = new OstProto::DeviceGroupIdList;
deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getDeletedDeviceGroupsSinceLastSync(*deviceGroupIdList);
@@ -524,6 +549,8 @@ void PortGroup::when_configApply(int portIndex)
delete deviceGroupIdList;
qDebug("applying 'new deviceGroups' ...");
+ logInfo(id(), mPorts[portIndex]->id(),
+ QString("Creating new DeviceGroups"));
deviceGroupIdList = new OstProto::DeviceGroupIdList;
deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getNewDeviceGroupsSinceLastSync(*deviceGroupIdList);
@@ -539,6 +566,8 @@ void PortGroup::when_configApply(int portIndex)
delete deviceGroupIdList;
qDebug("applying 'modified deviceGroups' ...");
+ logInfo(id(), mPorts[portIndex]->id(),
+ QString("Modifying changed DeviceGroups"));
deviceGroupConfigList = new OstProto::DeviceGroupConfigList;
deviceGroupConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id());
mPorts[portIndex]->getModifiedDeviceGroupsSinceLastSync(
@@ -561,6 +590,7 @@ void PortGroup::when_configApply(int portIndex)
// Update/Sync Streams
//
qDebug("applying 'deleted streams' ...");
+ logInfo(id(), mPorts[portIndex]->id(), QString("Deleting old Streams"));
streamIdList = new OstProto::StreamIdList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamIdList, ack);
@@ -572,6 +602,7 @@ void PortGroup::when_configApply(int portIndex)
NewCallback(this, &PortGroup::processDeleteStreamAck, controller));
qDebug("applying 'new streams' ...");
+ logInfo(id(), mPorts[portIndex]->id(), QString("Creating new Streams"));
streamIdList = new OstProto::StreamIdList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamIdList, ack);
@@ -583,6 +614,8 @@ void PortGroup::when_configApply(int portIndex)
NewCallback(this, &PortGroup::processAddStreamAck, controller));
qDebug("applying 'modified streams' ...");
+ logInfo(id(), mPorts[portIndex]->id(),
+ QString("Modifying changed Streams"));
streamConfigList = new OstProto::StreamConfigList;
ack = new OstProto::Ack;
controller = new PbRpcController(streamConfigList, ack);
@@ -633,6 +666,7 @@ void PortGroup::processModifyStreamAck(int portIndex,
qDebug("In %s", __FUNCTION__);
qDebug("apply completed");
+ logInfo(id(), mPorts[portIndex]->id(), QString("All port changes applied"));
mPorts[portIndex]->when_syncComplete();
mainWindow->setEnabled(true);
@@ -682,6 +716,9 @@ void PortGroup::processDeviceList(int portIndex, PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("getDeviceList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -716,6 +753,9 @@ void PortGroup::processDeviceNeighbors(
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("getPortIdList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -769,6 +809,8 @@ void PortGroup::processModifyPortAck(bool restoreUi,PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), QString("modifyPort RPC failed: %1")
+ .arg(controller->ErrorString()));
}
if (restoreUi) {
@@ -789,6 +831,8 @@ void PortGroup::processUpdatedPortConfig(PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), QString("getPortConfig RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -837,6 +881,9 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("geStreamIdList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -1051,6 +1098,9 @@ void PortGroup::processStreamConfigList(int portIndex,
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("getStreamConfigList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -1121,6 +1171,9 @@ void PortGroup::processDeviceGroupIdList(
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("getDeviceGroupIdList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -1215,6 +1268,9 @@ void PortGroup::processDeviceGroupConfigList(int portIndex,
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(controller->ErrorString()));
+ logError(id(), mPorts[portIndex]->id(),
+ QString("getDeviceGroupConfigList RPC failed: %1")
+ .arg(controller->ErrorString()));
goto _exit;
}
@@ -1436,14 +1492,17 @@ void PortGroup::processViewCaptureAck(PbRpcController *controller)
if (!QFile::exists(viewer))
{
+ logError(QString("Wireshark does not exist at %1").arg(viewer));
QMessageBox::warning(NULL, "Can't find Wireshark",
viewer + QString(" does not exist!\n\nPlease correct the path"
" to Wireshark in the Preferences."));
goto _exit;
}
- if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName()))
+ if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) {
qDebug("Failed starting Wireshark");
+ logError(QString("Failed to start %1").arg(viewer));
+ }
_exit:
delete controller;
@@ -1553,6 +1612,8 @@ void PortGroup::processPortStatsList()
{
qDebug("%s: rpc failed(%s)", __FUNCTION__,
qPrintable(statsController->ErrorString()));
+ logError(id(), QString("getPortStatsList RPC failed: %1")
+ .arg(statsController->ErrorString()));
goto _error_exit;
}
diff --git a/client/portmodel.cpp b/client/portmodel.cpp
index ae3d81e..da371d2 100644
--- a/client/portmodel.cpp
+++ b/client/portmodel.cpp
@@ -22,6 +22,7 @@ along with this program. If not, see
#include
#include
+#include
#if 0
#define DBG0(x) qDebug(x)
@@ -35,23 +36,6 @@ PortModel::PortModel(PortGroupList *p, QObject *parent)
: QAbstractItemModel(parent)
{
pgl = p;
-
- portIconFactory[OstProto::LinkStateUnknown][false] =
- QIcon(":/icons/bullet_white.png");
- portIconFactory[OstProto::LinkStateDown][false] =
- QIcon(":/icons/bullet_red.png");
- portIconFactory[OstProto::LinkStateUp][false] =
- QIcon(":/icons/bullet_green.png");
-
- for (int linkState = 0; linkState < kLinkStatesCount; linkState++)
- {
- QPixmap pixmap(":/icons/deco_exclusive.png");
- QPainter painter(&pixmap);
- QIcon icon = portIconFactory[linkState][false];
-
- painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32)));
- portIconFactory[linkState][true] = QIcon(pixmap);
- }
}
int PortModel::rowCount(const QModelIndex &parent) const
@@ -185,7 +169,11 @@ QVariant PortModel::data(const QModelIndex &index, int role) const
}
else if (role == Qt::DecorationRole)
{
- return portIconFactory[port->linkState()][port->hasExclusiveControl()];
+ return statusIcon(
+ port->linkState(),
+ port->hasExclusiveControl(),
+ port->isTransmitting(),
+ port->isCapturing());
}
else if (role == Qt::ForegroundRole)
{
@@ -294,7 +282,50 @@ quint32 PortModel::portId(const QModelIndex& index)
return (index.internalId()) & 0xFFFF;
}
+QPixmap PortModel::statusIcon(
+ int linkState, bool exclusive, bool transmit, bool capture) const
+{
+ QPixmap pixmap;
+ QString key = QString("$ost:portStatusIcon:%1:%2:%3:%4")
+ .arg(linkState).arg(exclusive).arg(transmit).arg(capture);
+ if (QPixmapCache::find(key, pixmap))
+ return pixmap;
+
+ static int sz = QPixmap(":/icons/frag_link_up.png").width();
+
+ // All frag_* icons must be of same size and can be overlayed
+ // on top of each other; assume square icons
+
+ pixmap = QPixmap(sz, sz);
+ pixmap.fill(Qt::transparent);
+
+ QPainter painter(&pixmap);
+
+ switch (linkState) {
+ case OstProto::LinkStateUp:
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_link_up.png"));
+ break;
+ case OstProto::LinkStateDown:
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_link_down.png"));
+ break;
+ case OstProto::LinkStateUnknown:
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_link_unknown.png"));
+ break;
+ }
+
+ if (exclusive)
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_exclusive.png"));
+
+ if (transmit)
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_transmit.png"));
+
+ if (capture)
+ painter.drawPixmap(0, 0, QPixmap(":/icons/frag_capture.png"));
+
+ QPixmapCache::insert(key, pixmap);
+ return pixmap;
+}
// ----------------------------------------------
// Slots
diff --git a/client/portmodel.h b/client/portmodel.h
index 4464fdf..0bfdf6a 100644
--- a/client/portmodel.h
+++ b/client/portmodel.h
@@ -50,12 +50,6 @@ public:
quint32 portGroupId(const QModelIndex& index);
quint32 portId(const QModelIndex& index);
-private:
- PortGroupList *pgl;
- static const int kLinkStatesCount = 3;
- static const int kExclusiveStatesCount = 2;
- QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount];
-
private slots:
// FIXME: these are invoked from outside - how come they are "private"?
void when_portGroupDataChanged(int portGroupId, int portId);
@@ -71,6 +65,12 @@ private slots:
void triggerLayoutAboutToBeChanged();
void triggerLayoutChanged();
#endif
+
+private:
+ QPixmap statusIcon(int linkState, bool exclusive,
+ bool transmit, bool capture) const;
+
+ PortGroupList *pgl;
};
#endif
diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp
index b91e763..1dcf197 100644
--- a/client/portstatsmodel.cpp
+++ b/client/portstatsmodel.cpp
@@ -20,6 +20,8 @@ along with this program. If not, see
#include "portstatsmodel.h"
#include "portgrouplist.h"
+#include
+#include
#include
enum {
@@ -94,15 +96,12 @@ void PortStatsModel::getDomainIndexes(const QModelIndex &index,
QVariant PortStatsModel::data(const QModelIndex &index, int role) const
{
- uint pgidx, pidx;
- int row;
-
// Check for a valid index
if (!index.isValid())
return QVariant();
// Check for row/column limits
- row = index.row();
+ int row = index.row();
if (row >= e_STAT_MAX)
return QVariant();
@@ -112,15 +111,22 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
if (index.column() >= (numPorts.last()))
return QVariant();
+ if (role == Qt::TextAlignmentRole)
+ {
+ if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
+ return Qt::AlignRight; // right-align numbers
+ else
+ return Qt::AlignHCenter; // center-align everything else
+ }
+
+ uint pgidx, pidx;
getDomainIndexes(index, pgidx, pidx);
+ OstProto::PortStats stats = pgl->mPortGroups.at(pgidx)
+ ->mPorts[pidx]->getStats();
// Check role
if (role == Qt::DisplayRole)
{
- OstProto::PortStats stats;
-
- stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats();
-
switch(row)
{
// Info
@@ -128,14 +134,8 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
return pgl->mPortGroups.at(pgidx)->mPorts[pidx]->userName();
// States
- case e_LINK_STATE:
- return LinkStateName.at(stats.state().link_state());
-
- case e_TRANSMIT_STATE:
- return BoolStateName.at(stats.state().is_transmit_on());
-
- case e_CAPTURE_STATE:
- return BoolStateName.at(stats.state().is_capture_on());
+ case e_COMBO_STATE:
+ return QVariant();
// Statistics
case e_STAT_FRAMES_RCVD:
@@ -201,12 +201,47 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
return 0;
}
}
- else if (role == Qt::TextAlignmentRole)
+ else if (role == Qt::DecorationRole)
{
- if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
- return Qt::AlignRight; // right-align numbers
+ if (row == e_COMBO_STATE)
+ return statusIcons(
+ stats.state().link_state(),
+ stats.state().is_transmit_on(),
+ stats.state().is_capture_on());
else
- return Qt::AlignHCenter; // center-align everything else
+ return QVariant();
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ if (row == e_COMBO_STATE) {
+ QString linkIcon;
+ switch (stats.state().link_state()) {
+ case OstProto::LinkStateUp:
+ linkIcon = ":/icons/bullet_green.png";
+ break;
+ case OstProto::LinkStateDown:
+ linkIcon = ":/icons/bullet_red.png";
+ break;
+ case OstProto::LinkStateUnknown:
+ linkIcon = ":/icons/bullet_white.png";
+ break;
+ }
+ // FIXME: Ideally, the text should be vertically centered wrt icon
+ // but style='vertical-align:middle for the img tag doesn't work
+ QString tooltip = QString(" Link %2")
+ .arg(linkIcon)
+ .arg(LinkStateName.at(
+ stats.state().link_state()));
+ if (stats.state().is_transmit_on())
+ tooltip.prepend(""
+ " Transmit On
");
+ if (stats.state().is_capture_on())
+ tooltip.append("
"
+ " Capture On");
+ return tooltip;
+ }
+ else
+ return QVariant();
}
else
return QVariant();
@@ -351,3 +386,49 @@ void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/)
emit dataChanged(topLeft, bottomRight);
}
+
+QPixmap PortStatsModel::statusIcons(
+ int linkState, bool transmit, bool capture) const
+{
+ QPixmap pixmap;
+ QString key = QString("$ost:statusList:%1:%2:%3")
+ .arg(linkState).arg(transmit).arg(capture);
+
+ if (QPixmapCache::find(key, pixmap))
+ return pixmap;
+
+ static int sz = QPixmap(":/icons/transmit_on.png").width();
+
+ // Assume all icons are of same size and are square
+ QPixmap blank(sz, sz);
+ blank.fill(Qt::transparent);
+
+ pixmap = QPixmap(sz*3, sz);
+ pixmap.fill(Qt::transparent);
+
+ QPainter painter(&pixmap);
+
+ painter.drawPixmap(0, 0,
+ transmit ? QPixmap(":/icons/transmit_on.png") : blank);
+
+ switch (linkState) {
+ case OstProto::LinkStateUp:
+ painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_green.png"));
+ break;
+ case OstProto::LinkStateDown:
+ painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_red.png"));
+ break;
+ case OstProto::LinkStateUnknown:
+ painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_white.png"));
+ break;
+ default:
+ painter.drawPixmap(sz, 0, blank);
+ }
+
+ painter.drawPixmap(sz*2, 0,
+ capture ? QPixmap(":/icons/sound_none.png") : blank);
+
+
+ QPixmapCache::insert(key, pixmap);
+ return pixmap;
+}
diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h
index b37e450..e4dbaa2 100644
--- a/client/portstatsmodel.h
+++ b/client/portstatsmodel.h
@@ -36,11 +36,9 @@ typedef enum {
// State
e_STATE_START,
- e_LINK_STATE = e_STATE_START,
- e_TRANSMIT_STATE,
- e_CAPTURE_STATE,
+ e_COMBO_STATE = e_STATE_START,
- e_STATE_END = e_CAPTURE_STATE,
+ e_STATE_END = e_COMBO_STATE,
// Statistics
e_STATISTICS_START,
@@ -77,9 +75,7 @@ typedef enum {
static QStringList PortStatName = (QStringList()
<< "User"
- << "Link State"
- << "Transmit State"
- << "Capture State"
+ << "Status"
<< "Frames Received"
<< "Frames Sent"
@@ -110,11 +106,6 @@ static QStringList LinkStateName = (QStringList()
<< "Up"
);
-static QStringList BoolStateName = (QStringList()
- << "Off"
- << "On"
-);
-
class PortGroupList;
class PortStatsModel : public QAbstractTableModel
@@ -160,6 +151,7 @@ class PortStatsModel : public QAbstractTableModel
void getDomainIndexes(const QModelIndex &index,
uint &portGroupIdx, uint &portIdx) const;
+ QPixmap statusIcons(int linkState, bool transmit, bool capture) const;
};
diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp
index 13b9eeb..aff6a83 100644
--- a/client/portstatswindow.cpp
+++ b/client/portstatswindow.cpp
@@ -20,6 +20,7 @@ along with this program. If not, see
#include "portstatswindow.h"
+#include "icononlydelegate.h"
#include "portstatsfilterdialog.h"
#include "portstatsmodel.h"
#include "portstatsproxymodel.h"
@@ -53,6 +54,24 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
tvPortStats->verticalHeader()->setHighlightSections(false);
tvPortStats->verticalHeader()->setDefaultSectionSize(
tvPortStats->verticalHeader()->minimumSectionSize());
+
+ statusDelegate = new IconOnlyDelegate(this);
+#if 0
+ // XXX: Ideally we should use this, but it doesn't work because in
+ // this constructor, the port model is empty and model->index() returns
+ // an invalid index ...
+ tvPortStats->setItemDelegateForRow(
+ proxyStatsModel ?
+ proxyStatsModel->mapFromSource(model->index(e_COMBO_STATE, 0))
+ .row() :
+ e_COMBO_STATE,
+ statusDelegate);
+#else
+ // ... so we use this hard-coded hack
+ tvPortStats->setItemDelegateForRow(
+ proxyStatsModel ? e_COMBO_STATE-1 : e_COMBO_STATE,
+ statusDelegate);
+#endif
connect(tvPortStats->selectionModel(),
SIGNAL(selectionChanged(
@@ -65,6 +84,7 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
PortStatsWindow::~PortStatsWindow()
{
delete proxyStatsModel;
+ delete statusDelegate;
}
/* ------------- SLOTS (public) -------------- */
@@ -205,6 +225,13 @@ void PortStatsWindow::on_tbResolveNeighbors_clicked()
{
pgl->portGroupByIndex(portList.at(i).portGroupId).
resolveDeviceNeighbors(&portList[i].portList);
+
+ // Update device info for the just processed portgroup
+ for (int j = 0; j < portList[i].portList.size(); j++)
+ {
+ pgl->portGroupByIndex(portList.at(i).portGroupId).
+ getDeviceInfo(portList[i].portList[j]);
+ }
}
}
@@ -220,6 +247,13 @@ void PortStatsWindow::on_tbClearNeighbors_clicked()
{
pgl->portGroupByIndex(portList.at(i).portGroupId).
clearDeviceNeighbors(&portList[i].portList);
+
+ // Update device info for the just processed portgroup
+ for (int j = 0; j < portList[i].portList.size(); j++)
+ {
+ pgl->portGroupByIndex(portList.at(i).portGroupId).
+ getDeviceInfo(portList[i].portList[j]);
+ }
}
}
diff --git a/client/portstatswindow.h b/client/portstatswindow.h
index 6efb263..a7b8fe3 100644
--- a/client/portstatswindow.h
+++ b/client/portstatswindow.h
@@ -27,6 +27,7 @@ along with this program. If not, see
#include "portstatsmodel.h"
class QSortFilterProxyModel;
+class QStyledItemDelegate;
class PortStatsWindow : public QWidget, public Ui::PortStatsWindow
{
@@ -63,6 +64,7 @@ private:
PortGroupList *pgl;
PortStatsModel *model;
QSortFilterProxyModel *proxyStatsModel;
+ QStyledItemDelegate *statusDelegate;
QModelIndexList selectedColumns;
};
diff --git a/client/portswindow.cpp b/client/portswindow.cpp
index 4ca85cb..6fe781b 100644
--- a/client/portswindow.cpp
+++ b/client/portswindow.cpp
@@ -28,6 +28,8 @@ along with this program. If not, see
#include "fileformat.pb.h"
+#include "xqlocale.h"
+
#include
#include
#include
@@ -435,7 +437,7 @@ void PortsWindow::on_averagePacketsPerSec_editingFinished()
Q_ASSERT(plm->isPort(current));
bool isOk;
- double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk);
+ double pps = XLocale().toDouble(averagePacketsPerSec->text(), &isOk);
plm->port(current).setAveragePacketRate(pps);
}
@@ -450,7 +452,7 @@ void PortsWindow::on_averageBitsPerSec_editingFinished()
Q_ASSERT(plm->isPort(current));
bool isOk;
- double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk);
+ double bps = XLocale().toDouble(averageBitsPerSec->text(), &isOk);
plm->port(current).setAverageBitRate(bps);
}
diff --git a/client/portswindow.ui b/client/portswindow.ui
index f383271..6a3c0ca 100644
--- a/client/portswindow.ui
+++ b/client/portswindow.ui
@@ -57,7 +57,7 @@
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
<p>To create a stream, select the port on which you want to send packets.</p>
<hr/>
-<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p>
+<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="https://jump.ostinato.org/noports">Get Help!</a></p>
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
@@ -96,7 +96,7 @@
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
<p>To create a stream, select the port on which you want to send packets. </p>
<hr/>
-<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p>
+<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="https://jump.ostinato.org/noports">Get Help!</a></p>
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp
index 7b6a209..a38a77d 100644
--- a/client/streamconfigdialog.cpp
+++ b/client/streamconfigdialog.cpp
@@ -30,6 +30,8 @@ along with this program. If not, see
#include "../common/protocolmanager.h"
#include "../common/protocolwidgetfactory.h"
+#include "xqlocale.h"
+
#include
#include
@@ -1124,9 +1126,9 @@ void StreamConfigDialog::StoreCurrentStream()
pStream->setNumBursts(leNumBursts->text().toULong(&isOk));
pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk));
pStream->setPacketRate(
- QLocale().toDouble(lePacketsPerSec->text(), &isOk));
+ XLocale().toDouble(lePacketsPerSec->text(), &isOk));
pStream->setBurstRate(
- QLocale().toDouble(leBurstsPerSec->text(), &isOk));
+ XLocale().toDouble(leBurstsPerSec->text(), &isOk));
}
}
@@ -1172,7 +1174,7 @@ void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text)
if (rbSendPackets->isChecked())
{
- double pktsPerSec = QLocale().toDouble(text, &isOk);
+ double pktsPerSec = XLocale().toDouble(text, &isOk);
double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8);
if (rbPacketsPerSec->isChecked())
@@ -1197,7 +1199,7 @@ void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text)
if (rbSendBursts->isChecked())
{
- double burstsPerSec = QLocale().toDouble(text, &isOk);
+ double burstsPerSec = XLocale().toDouble(text, &isOk);
double bitsPerSec = burstsPerSec *
double(burstSize * (frameLen + kEthFrameOverHead) * 8);
if (rbBurstsPerSec->isChecked())
@@ -1222,13 +1224,13 @@ void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text)
if (rbSendPackets->isChecked())
{
- double pktsPerSec = QLocale().toDouble(text, &isOk)/
+ double pktsPerSec = XLocale().toDouble(text, &isOk)/
double((frameLen+kEthFrameOverHead)*8);
lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4));
}
else if (rbSendBursts->isChecked())
{
- double burstsPerSec = QLocale().toDouble(text, &isOk)/
+ double burstsPerSec = XLocale().toDouble(text, &isOk)/
double(burstSize * (frameLen + kEthFrameOverHead) * 8);
leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4));
}
diff --git a/client/xqlocale.h b/client/xqlocale.h
new file mode 100644
index 0000000..c313964
--- /dev/null
+++ b/client/xqlocale.h
@@ -0,0 +1,35 @@
+/*
+Copyright (C) 2018 Srivats P.
+
+This file is part of "Ostinato"
+
+This 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 3 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, see
+*/
+
+#ifndef _X_LOCALE_H
+#define _X_LOCALE_H
+
+#include
+
+class XLocale: public QLocale
+{
+public:
+ double toDouble(const QString &s, bool *ok = Q_NULLPTR) const {
+ QString s2 = s;
+ return QLocale::toDouble(s2.remove(groupSeparator()), ok);
+ }
+};
+
+#endif
+
diff --git a/client/xtableview.h b/client/xtableview.h
index 296ec54..3de3353 100644
--- a/client/xtableview.h
+++ b/client/xtableview.h
@@ -22,6 +22,8 @@ along with this program. If not, see
#include
+#include
+#include
#include
class XTableView : public QTableView
@@ -42,6 +44,31 @@ protected:
else
QTableView::paintEvent(event);
}
+
+ virtual void keyPressEvent(QKeyEvent *event)
+ {
+ // Copy selection to clipboard (base class copies only current item)
+ if (event->matches(QKeySequence::Copy)
+ && selectionBehavior() == SelectRows) {
+ QString text;
+ int lastRow = -1;
+ QModelIndexList selected = selectionModel()->selectedIndexes();
+ qSort(selected);
+ foreach(QModelIndex index, selected) {
+ if (index.row() != lastRow) {
+ if (!text.isEmpty())
+ text.append("\n");
+ }
+ else
+ text.append("\t");
+ text.append(model()->data(index).toString());
+ lastRow = index.row();
+ }
+ qApp->clipboard()->setText(text);
+ }
+ else
+ QTableView::keyPressEvent(event);
+ }
};
#endif
diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp
index 2de699b..477a199 100644
--- a/common/abstractprotocol.cpp
+++ b/common/abstractprotocol.cpp
@@ -931,18 +931,28 @@ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex,
CksumType cksumType, CksumScope cksumScope) const
{
quint32 sum = 0;
- quint16 cksum;
+ quint32 cksum;
AbstractProtocol *p = prev;
Q_ASSERT(cksumType == CksumIpPseudo);
+ // We may have extension headers between us and the IP header - skip 'em
while (p)
{
cksum = p->protocolFrameCksum(streamIndex, cksumType);
- sum += (quint16) ~cksum;
- qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum);
- if (cksumScope == CksumScopeAdjacentProtocol)
- goto out;
+ if (cksum <= 0xFFFF) // protocol has a valid pseudo cksum ie its IP
+ {
+ sum += (quint16) ~cksum;
+ // Ip4/6Protocol::protocolFrameCksum(CksumIpPseudo) only
+ // counts the src/dst IP (see Note in there)
+ // Count the payload length and protocolId here
+ sum += protocolFrameSize(streamIndex)
+ + protocolFramePayloadSize(streamIndex);
+ sum += protocolId(ProtocolIdIp);
+ qDebug("%s: sum = %x, cksum = %x", __FUNCTION__, sum, cksum);
+ if (cksumScope == CksumScopeAdjacentProtocol)
+ goto out;
+ }
p = p->prev;
}
if (parent)
diff --git a/common/hexdump.cpp b/common/hexdump.cpp
index d8f882f..12709d2 100644
--- a/common/hexdump.cpp
+++ b/common/hexdump.cpp
@@ -109,8 +109,8 @@ QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib,
case FieldValue:
case FieldTextValue:
case FieldFrameValue:
- ba.append(QString().fromStdString(data.content()));
- if (data.pad_until_end())
+ ba.append(data.content().c_str(), data.content().length());
+ if (padUntilEnd())
{
pad = QByteArray(
protocolFrameSize(streamIndex) - ba.size(), '\0');
@@ -144,7 +144,7 @@ QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib,
switch(attrib)
{
case FieldValue:
- return data.pad_until_end();
+ return padUntilEnd();
default:
break;
}
@@ -197,7 +197,7 @@ int HexDumpProtocol::protocolFrameSize(int streamIndex) const
{
int len = data.content().size();
- if (data.pad_until_end())
+ if (padUntilEnd())
{
int pad = mpStream->frameLen(streamIndex)
- (protocolFrameOffset(streamIndex)
@@ -212,3 +212,10 @@ int HexDumpProtocol::protocolFrameSize(int streamIndex) const
return len;
}
+bool HexDumpProtocol::padUntilEnd() const
+{
+ if (next)
+ return false; // No padding if we are not the last protocol
+
+ return data.pad_until_end();
+}
diff --git a/common/hexdump.h b/common/hexdump.h
index 4318192..38fbbf6 100644
--- a/common/hexdump.h
+++ b/common/hexdump.h
@@ -68,6 +68,8 @@ public:
virtual int protocolFrameSize(int streamIndex = 0) const;
private:
+ bool padUntilEnd() const;
+
OstProto::HexDump data;
};
#endif
diff --git a/common/ip4.cpp b/common/ip4.cpp
index cdeea98..0d4063f 100644
--- a/common/ip4.cpp
+++ b/common/ip4.cpp
@@ -521,7 +521,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
case FieldValue:
case FieldFrameValue:
case FieldTextValue:
- ba.append(QString().fromStdString(data.options()));
+ ba.append(data.options().c_str(), data.options().length());
default:
break;
}
@@ -859,14 +859,18 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
sum += *((quint16*)(p + 14)); // src-ip lo
sum += *((quint16*)(p + 16)); // dst-ip hi
sum += *((quint16*)(p + 18)); // dst-ip lo
- sum += qToBigEndian((quint16)
- protocolFramePayloadSize(streamIndex)); // len
- sum += qToBigEndian((quint16) *(p + 9)); // proto
+
+ // XXX: payload length and protocol are also part of the
+ // pseudo cksum but for IPv6 we need to skip extension headers to
+ // get to them, so these two fields are counted in the
+ // pseudo cksum in AbstractProtocol::protocolFrameHeaderCksum()
+ // Although not needed for IPv4 case, we do the same for IPv4
+ // also, so that code there is common for IPv4 and IPv6
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
- return ~qFromBigEndian((quint16)sum);
+ return qFromBigEndian((quint16) ~sum);
}
default:
break;
diff --git a/common/ip4pdml.cpp b/common/ip4pdml.cpp
index eefbd6e..a47a83c 100644
--- a/common/ip4pdml.cpp
+++ b/common/ip4pdml.cpp
@@ -63,8 +63,11 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/,
else if ((name == "ip.options") ||
attributes.value("show").toString().startsWith("Options"))
{
- options_ = QByteArray::fromHex(
+ OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4);
+
+ QByteArray options = QByteArray::fromHex(
attributes.value("value").toString().toUtf8());
+ ip4->set_options(options.constData(), options.size());
}
else if (name == "ip.flags")
{
@@ -75,7 +78,7 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/,
}
void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto,
- OstProto::Stream *stream)
+ OstProto::Stream* /*stream*/)
{
OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4);
@@ -84,20 +87,5 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto,
ip4->set_is_override_totlen(true);
ip4->set_is_override_proto(true);
ip4->set_is_override_cksum(true);
-
- if (options_.size())
- {
- OstProto::Protocol *proto = stream->add_protocol();
-
- proto->mutable_protocol_id()->set_id(
- OstProto::Protocol::kHexDumpFieldNumber);
-
- OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump);
-
- hexDump->mutable_content()->append(options_.constData(),
- options_.size());
- hexDump->set_pad_until_end(false);
- options_.resize(0);
- }
}
diff --git a/common/ip4pdml.h b/common/ip4pdml.h
index 64f818d..2aa3d04 100644
--- a/common/ip4pdml.h
+++ b/common/ip4pdml.h
@@ -34,8 +34,6 @@ public:
OstProto::Stream *stream);
protected:
PdmlIp4Protocol();
-private:
- QByteArray options_;
};
#endif
diff --git a/common/ip6.cpp b/common/ip6.cpp
index 3bbf7d3..6ec1587 100644
--- a/common/ip6.cpp
+++ b/common/ip6.cpp
@@ -781,13 +781,18 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
// src-ip, dst-ip
for (int i = 8; i < fv.size(); i+=2)
sum += *((quint16*)(p + i));
- sum += *((quint16*)(p + 4)); // payload len
- sum += qToBigEndian((quint16) *(p + 6)); // proto
+
+ // XXX: payload length and protocol are also part of the
+ // pseudo cksum but we need to skip extension headers to
+ // get to them as per RFC 8200 Section 8.1
+ // Since we can't traverse beyond our immediate neighboring
+ // protocol from here, these two fields are counted in the
+ // pseudo cksum in AbstractProtocol::protocolFrameHeaderCksum()
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
- return ~qFromBigEndian((quint16)sum);
+ return qFromBigEndian((quint16) ~sum);
}
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
}
diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp
index 1db047d..c6eba2b 100644
--- a/common/pcapfileformat.cpp
+++ b/common/pcapfileformat.cpp
@@ -438,6 +438,8 @@ _non_pdml:
stream->mutable_core()->set_is_enabled(true);
stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS
+ stream->mutable_control()->set_num_packets(1);
+
// setup packet rate to the timing in pcap (as close as possible)
const uint kUsecsInSec = uint(1e6);
uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec);
diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp
index e81351e..ec1f99d 100644
--- a/common/pdmlprotocols.cpp
+++ b/common/pdmlprotocols.cpp
@@ -192,4 +192,6 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/,
}
}
}
+ else if (name == "frame.number")
+ stream->mutable_control()->set_num_packets(1);
}
diff --git a/options.pri b/options.pri
index 04ee84a..041da36 100644
--- a/options.pri
+++ b/options.pri
@@ -1,2 +1,2 @@
-QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]
+QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS] -std=c++11
CONFIG(debug, debug|release): QMAKE_CXXFLAGS_WARN_ON += -Wall -W -Wextra -Werror