Merge branch 'master' into hostdev
@ -104,7 +104,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="LinksLabel" >
|
||||
<property name="text" >
|
||||
<string><a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a></string>
|
||||
<string><a href="https://ostinato.org">ostinato.org</a><br><a href="https://twitter.com/ostinato">@ostinato</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><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></string>
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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
@ -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
|
||||
|
Before Width: | Height: | Size: 793 B |
BIN
client/icons/donate.png
Normal file
After Width: | Height: | Size: 732 B |
BIN
client/icons/frag_capture.png
Normal file
After Width: | Height: | Size: 268 B |
BIN
client/icons/frag_exclusive.png
Normal file
After Width: | Height: | Size: 317 B |
BIN
client/icons/frag_link_down.png
Normal file
After Width: | Height: | Size: 293 B |
BIN
client/icons/frag_link_unknown.png
Normal file
After Width: | Height: | Size: 253 B |
BIN
client/icons/frag_link_up.png
Normal file
After Width: | Height: | Size: 316 B |
BIN
client/icons/frag_transmit.png
Normal file
After Width: | Height: | Size: 286 B |
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 435 B |
BIN
client/icons/transmit_on.png
Normal file
After Width: | Height: | Size: 749 B |
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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>
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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" />
|
||||
|
@ -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 \
|
||||
|
@ -14,12 +14,18 @@
|
||||
<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/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>
|
||||
@ -44,5 +50,6 @@
|
||||
<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>
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
@ -54,6 +55,24 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
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(
|
||||
const QItemSelection&, const QItemSelection&)),
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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></string>
|
||||
<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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
@ -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></string>
|
||||
<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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
if (cksum <= 0xFFFF) // protocol has a valid pseudo cksum ie its IP
|
||||
{
|
||||
sum += (quint16) ~cksum;
|
||||
qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, 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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -68,6 +68,8 @@ public:
|
||||
virtual int protocolFrameSize(int streamIndex = 0) const;
|
||||
|
||||
private:
|
||||
bool padUntilEnd() const;
|
||||
|
||||
OstProto::HexDump data;
|
||||
};
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,6 @@ public:
|
||||
OstProto::Stream *stream);
|
||||
protected:
|
||||
PdmlIp4Protocol();
|
||||
private:
|
||||
QByteArray options_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -192,4 +192,6 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (name == "frame.number")
|
||||
stream->mutable_control()->set_num_packets(1);
|
||||
}
|
||||
|
@ -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
|
||||
|