Merge branch 'master' into hostdev

This commit is contained in:
Srivats P 2019-02-05 20:34:00 +05:30
commit 90d93d52e1
53 changed files with 1236 additions and 172 deletions

View File

@ -104,7 +104,7 @@
<item>
<widget class="QLabel" name="LinksLabel" >
<property name="text" >
<string>&lt;a href="http://ostinato.org">http://ostinato.org&lt;/a>&lt;br>&lt;a href="http://twitter.com/ostinato">@ostinato&lt;/a></string>
<string>&lt;a href="https://ostinato.org">ostinato.org&lt;/a>&lt;br>&lt;a href="https://twitter.com/ostinato">@ostinato&lt;/a></string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
@ -155,7 +155,7 @@ Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/)</string>
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>&lt;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.&lt;/p>&lt;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.&lt;/p>&lt;p>You should have received a copy of the GNU General Public License along with this program. If not, see &lt;a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/&lt;/a>&lt;/p></string>
<string>&lt;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.&lt;/p>&lt;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.&lt;/p>&lt;p>You should have received a copy of the GNU General Public License along with this program. If not, see &lt;a href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/&lt;/a>&lt;/p></string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>

View File

@ -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<int>(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<int>(Qt::AlignRight|Qt::AlignVCenter);
default:
break;
}

View File

@ -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<uint> 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<uint> 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);
}

View File

@ -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_;
};

View File

@ -36,19 +36,6 @@
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>131</width>
<height>23</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="refresh">
<property name="toolTip">
@ -66,6 +53,60 @@
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>131</width>
<height>23</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="arpNdpLabel">
<property name="text">
<string>ARP/ND</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="resolveNeighbors">
<property name="toolTip">
<string>Resolve Neighbors</string>
</property>
<property name="statusTip">
<string>Resolve Device Neighbors on selected port(s)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/neighbor_resolve.png</normaloff>:/icons/neighbor_resolve.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clearNeighbors">
<property name="toolTip">
<string>Clear Neighbors</string>
</property>
<property name="statusTip">
<string>Clear Device Neighbors on selected port(s)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/neighbor_clear.png</normaloff>:/icons/neighbor_clear.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>

40
client/icononlydelegate.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#ifndef _ICON_ONLY_DELEGATE
#define _ICON_ONLY_DELEGATE
#include <QStyledItemDelegate>
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 B

BIN
client/icons/donate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

View File

@ -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);

87
client/log.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#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

145
client/logsmodel.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#include "logsmodel.h"
#include <QBrush>
// 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<LogLevel>(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();
}

63
client/logsmodel.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#ifndef _LOGS_MODEL_H
#define _LOGS_MODEL_H
#include <QAbstractTableModel>
#include <QTime>
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<Log> logs_;
LogLevel currentLevel_{kInfo};
};
#endif

114
client/logswindow.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#include "logswindow.h"
#include "logsmodel.h"
#include "modeltest.h"
#include <QDockWidget>
#include <QHeaderView>
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<QDockWidget*>(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()));
}
}

50
client/logswindow.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#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

137
client/logswindow.ui Normal file
View File

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogsWindow</class>
<widget class="QWidget" name="LogsWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>693</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Level</string>
</property>
<property name="buddy">
<cstring>level</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="level">
<property name="toolTip">
<string>Log Level</string>
</property>
<property name="statusTip">
<string>Select the desired logging level</string>
</property>
<item>
<property name="text">
<string>Info</string>
</property>
</item>
<item>
<property name="text">
<string>Warning</string>
</property>
</item>
<item>
<property name="text">
<string>Error</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoScroll">
<property name="text">
<string>Auto Scroll</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>429</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="clear">
<property name="toolTip">
<string>Clear Logs</string>
</property>
<property name="statusTip">
<string>Clear Logs</string>
</property>
<property name="text">
<string>Clear</string>
</property>
<property name="icon">
<iconset resource="ostinato.qrc">
<normaloff>:/icons/portstats_clear_all.png</normaloff>:/icons/portstats_clear_all.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="XTableView" name="logs">
<property name="whatsThis">
<string>Ostinato application logs are displayed here</string>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>XTableView</class>
<extends>QTableView</extends>
<header>xtableview.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>level</tabstop>
<tabstop>autoScroll</tabstop>
<tabstop>clear</tabstop>
<tabstop>logs</tabstop>
</tabstops>
<resources>
<include location="ostinato.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -24,6 +24,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#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;

View File

@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QMainWindow>
#include <QProcess>
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:

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0" >
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
@ -33,6 +34,8 @@
</property>
<addaction name="actionHelpOnline" />
<addaction name="separator" />
<addaction name="actionDonate" />
<addaction name="separator" />
<addaction name="actionHelpAbout" />
<addaction name="actionAboutQt" />
</widget>
@ -111,6 +114,14 @@
<string>Help (Online)</string>
</property>
</action>
<action name="actionDonate" >
<property name="icon" >
<iconset resource="ostinato.qrc" >:/icons/donate.png</iconset>
</property>
<property name="text" >
<string>Donate</string>
</property>
</action>
</widget>
<resources>
<include location="ostinato.qrc" />

View File

@ -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 \

View File

@ -1,48 +1,55 @@
<RCC>
<qresource prefix="/" >
<file>icons/about.png</file>
<file>icons/add.png</file>
<file>icons/arrow_down.png</file>
<file>icons/arrow_left.png</file>
<file>icons/arrow_right.png</file>
<file>icons/arrow_up.png</file>
<file>icons/bullet_error.png</file>
<file>icons/bullet_green.png</file>
<file>icons/bullet_orange.png</file>
<file>icons/bullet_red.png</file>
<file>icons/bullet_white.png</file>
<file>icons/bullet_yellow.png</file>
<file>icons/control_play.png</file>
<file>icons/control_stop.png</file>
<file>icons/deco_exclusive.png</file>
<file>icons/delete.png</file>
<file>icons/devicegroup_add.png</file>
<file>icons/devicegroup_delete.png</file>
<file>icons/devicegroup_edit.png</file>
<file>icons/exit.png</file>
<file>icons/gaps.png</file>
<file>icons/help.png</file>
<file>icons/logo.png</file>
<file>icons/magnifier.png</file>
<file>icons/name.png</file>
<file>icons/neighbor_clear.png</file>
<file>icons/neighbor_resolve.png</file>
<file>icons/portgroup_add.png</file>
<file>icons/portgroup_connect.png</file>
<file>icons/portgroup_delete.png</file>
<file>icons/portgroup_disconnect.png</file>
<file>icons/portstats_clear.png</file>
<file>icons/portstats_clear_all.png</file>
<file>icons/portstats_filter.png</file>
<file>icons/preferences.png</file>
<file>icons/qt.png</file>
<file>icons/refresh.png</file>
<file>icons/sound_mute.png</file>
<file>icons/sound_none.png</file>
<file>icons/stream_add.png</file>
<file>icons/stream_delete.png</file>
<file>icons/stream_duplicate.png</file>
<file>icons/stream_edit.png</file>
<file>icons/stream_stats.png</file>
</qresource>
<qresource prefix="/">
<file>icons/about.png</file>
<file>icons/add.png</file>
<file>icons/arrow_down.png</file>
<file>icons/arrow_left.png</file>
<file>icons/arrow_right.png</file>
<file>icons/arrow_up.png</file>
<file>icons/bullet_error.png</file>
<file>icons/bullet_green.png</file>
<file>icons/bullet_orange.png</file>
<file>icons/bullet_red.png</file>
<file>icons/bullet_white.png</file>
<file>icons/bullet_yellow.png</file>
<file>icons/control_play.png</file>
<file>icons/control_stop.png</file>
<file>icons/delete.png</file>
<file>icons/devicegroup_add.png</file>
<file>icons/devicegroup_delete.png</file>
<file>icons/devicegroup_edit.png</file>
<file>icons/donate.png</file>
<file>icons/exit.png</file>
<file>icons/frag_capture.png</file>
<file>icons/frag_exclusive.png</file>
<file>icons/frag_link_down.png</file>
<file>icons/frag_link_unknown.png</file>
<file>icons/frag_link_up.png</file>
<file>icons/frag_transmit.png</file>
<file>icons/gaps.png</file>
<file>icons/help.png</file>
<file>icons/logo.png</file>
<file>icons/magnifier.png</file>
<file>icons/name.png</file>
<file>icons/neighbor_clear.png</file>
<file>icons/neighbor_resolve.png</file>
<file>icons/portgroup_add.png</file>
<file>icons/portgroup_connect.png</file>
<file>icons/portgroup_delete.png</file>
<file>icons/portgroup_disconnect.png</file>
<file>icons/portstats_clear.png</file>
<file>icons/portstats_clear_all.png</file>
<file>icons/portstats_filter.png</file>
<file>icons/preferences.png</file>
<file>icons/qt.png</file>
<file>icons/refresh.png</file>
<file>icons/sound_mute.png</file>
<file>icons/sound_none.png</file>
<file>icons/stream_add.png</file>
<file>icons/stream_delete.png</file>
<file>icons/stream_duplicate.png</file>
<file>icons/stream_edit.png</file>
<file>icons/stream_stats.png</file>
<file>icons/transmit_on.png</file>
</qresource>
</RCC>

View File

@ -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);

View File

@ -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()

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#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;
}

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QIcon>
#include <QPainter>
#include <QPixmapCache>
#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

View File

@ -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

View File

@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatsmodel.h"
#include "portgrouplist.h"
#include <QPainter>
#include <QPixmapCache>
#include <QTimer>
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("<img src='%1'/> Link %2")
.arg(linkIcon)
.arg(LinkStateName.at(
stats.state().link_state()));
if (stats.state().is_transmit_on())
tooltip.prepend("<img src=':/icons/transmit_on.png'/>"
" Transmit On<br/>");
if (stats.state().is_capture_on())
tooltip.append("<br/><img src=':/icons/sound_none.png'/>"
" 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;
}

View File

@ -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;
};

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#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]);
}
}
}

View File

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#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;
};

View File

@ -28,6 +28,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "fileformat.pb.h"
#include "xqlocale.h"
#include <QFileInfo>
#include <QInputDialog>
#include <QItemSelectionModel>
@ -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);
}

View File

@ -57,7 +57,7 @@
&lt;p&gt;To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.&lt;/p&gt;
&lt;p&gt;To create a stream, select the port on which you want to send packets.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Don't see the port that you want (or any ports at all) inside the port group? &lt;a href=&quot;http://jump.ostinato.org/noports&quot;&gt;Get Help!&lt;/a&gt;&lt;/p&gt;</string>
&lt;p&gt;Don't see the port that you want (or any ports at all) inside the port group? &lt;a href=&quot;https://jump.ostinato.org/noports&quot;&gt;Get Help!&lt;/a&gt;&lt;/p&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
@ -96,7 +96,7 @@
&lt;p&gt;To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.&lt;/p&gt;
&lt;p&gt;To create a stream, select the port on which you want to send packets. &lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Don't see the port that you want (or any ports at all) inside the port group? &lt;a href=&quot;http://jump.ostinato.org/noports&quot;&gt;Get Help!&lt;/a&gt;&lt;/p&gt;</string>
&lt;p&gt;Don't see the port that you want (or any ports at all) inside the port group? &lt;a href=&quot;https://jump.ostinato.org/noports&quot;&gt;Get Help!&lt;/a&gt;&lt;/p&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>

View File

@ -30,6 +30,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "../common/protocolmanager.h"
#include "../common/protocolwidgetfactory.h"
#include "xqlocale.h"
#include <QButtonGroup>
#include <QMessageBox>
@ -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));
}

35
client/xqlocale.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>
*/
#ifndef _X_LOCALE_H
#define _X_LOCALE_H
#include <QLocale>
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

View File

@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QTableView>
#include <QClipboard>
#include <QKeyEvent>
#include <QPainter>
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

View File

@ -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)

View File

@ -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();
}

View File

@ -68,6 +68,8 @@ public:
virtual int protocolFrameSize(int streamIndex = 0) const;
private:
bool padUntilEnd() const;
OstProto::HexDump data;
};
#endif

View File

@ -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;

View File

@ -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);
}
}

View File

@ -34,8 +34,6 @@ public:
OstProto::Stream *stream);
protected:
PdmlIp4Protocol();
private:
QByteArray options_;
};
#endif

View File

@ -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);
}

View File

@ -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);

View File

@ -192,4 +192,6 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/,
}
}
}
else if (name == "frame.number")
stream->mutable_control()->set_num_packets(1);
}

View File

@ -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