Merge commit 'refs/subrepo/ostinato/fetch' into subrepo/ostinato
Update from master repo into turbo
This commit is contained in:
commit
701a058c7d
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at support@ostinato.org. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
16
.github/CONTRIBUTING.md
vendored
Normal file
16
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Contributing Guidelines
|
||||||
|
Please use a Pull Request to contribute code. Very small fixes (< 10 lines) can provide the diff via an issue instead of a PR.
|
||||||
|
|
||||||
|
In either case, you agree to the below legal terms and you indicate your acceptance by explicitly adding a comment to the issue or PR stating -
|
||||||
|
|
||||||
|
```
|
||||||
|
I have read the contributing guidelines (CONTRIBUTING.md) and I hereby assign the copyrights of these
|
||||||
|
changes to [Srivats P](https://github.com/pstavirs).
|
||||||
|
```
|
||||||
|
|
||||||
|
## Legal
|
||||||
|
By submitting a Pull Request, you disavow any rights or claims to any changes submitted to the Ostinato project and assign the copyright of those changes to [Srivats P](https://github.com/pstavirs).
|
||||||
|
|
||||||
|
If you cannot or do not want to reassign those rights (your employment contract for your employer may not allow this), you should not submit a PR. Open an issue and someone else can do the work.
|
||||||
|
|
||||||
|
This is a legal way of saying "_If you submit a PR to us, that code becomes ours_". 99.9% of the time that's what you intend anyways; we hope it doesn't scare you away from contributing.
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
github: pstavirs
|
13
.travis.yml
13
.travis.yml
@ -1,4 +1,5 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
|
osx_image: xcode11.3
|
||||||
|
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
@ -13,17 +14,14 @@ matrix:
|
|||||||
- os: osx
|
- os: osx
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
|
|
||||||
before_install:
|
before_script:
|
||||||
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then \
|
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then \
|
||||||
brew update && \
|
|
||||||
brew install qt5 && \
|
|
||||||
brew link qt5 --force && \
|
|
||||||
brew install protobuf && \
|
|
||||||
ls -lR /usr/local/include/google/protobuf; \
|
ls -lR /usr/local/include/google/protobuf; \
|
||||||
which clang++; \
|
which clang++; \
|
||||||
clang++ -E -x c++ - -v < /dev/null; \
|
clang++ -E -x c++ - -v < /dev/null; \
|
||||||
export CPLUS_INCLUDE_PATH=/usr/local/include; \
|
export CPLUS_INCLUDE_PATH=/usr/local/include; \
|
||||||
export LIBRARY_PATH=/usr/local/lib; \
|
export LIBRARY_PATH=/usr/local/lib; \
|
||||||
|
export PATH=/usr/local/opt/qt/bin:$PATH; \
|
||||||
fi"
|
fi"
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@ -37,7 +35,10 @@ addons:
|
|||||||
- protobuf-compiler
|
- protobuf-compiler
|
||||||
- libnl-3-dev
|
- libnl-3-dev
|
||||||
- libnl-route-3-dev
|
- libnl-route-3-dev
|
||||||
|
homebrew:
|
||||||
|
packages:
|
||||||
|
- qt5
|
||||||
|
- protobuf
|
||||||
script:
|
script:
|
||||||
- QT_SELECT=qt5 qmake -config debug
|
- QT_SELECT=qt5 qmake -config debug
|
||||||
- make
|
- make
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Ostinato
|
# Ostinato
|
||||||
|
|
||||||
[](https://travis-ci.org/pstavirs/ostinato)
|
[](https://app.travis-ci.com/pstavirs/ostinato)
|
||||||
|
|
||||||
This is the code repository for the Ostinato network packet crafter and traffic generator
|
This is the code repository for the Ostinato network packet crafter and traffic generator
|
||||||
|
|
||||||
|
@ -262,8 +262,10 @@ void DumpView::paintEvent(QPaintEvent* /*event*/)
|
|||||||
// FIXME(LOW): unable to set the self widget's font in constructor
|
// FIXME(LOW): unable to set the self widget's font in constructor
|
||||||
painter.setFont(QFont("Courier"));
|
painter.setFont(QFont("Courier"));
|
||||||
|
|
||||||
// set a white background
|
// Qt automatically clears the background before we are called
|
||||||
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
|
// QWidget::paintEvent doc:
|
||||||
|
// When the paint event occurs, the update region has normally
|
||||||
|
// been erased, so you are painting on the widget's background.
|
||||||
|
|
||||||
if (model())
|
if (model())
|
||||||
{
|
{
|
||||||
|
98
client/fieldedit.cpp
Normal file
98
client/fieldedit.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "fieldedit.h"
|
||||||
|
|
||||||
|
FieldEdit::FieldEdit(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
setType(kUInt64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldEdit::setType(FieldType type)
|
||||||
|
{
|
||||||
|
// clear existing contents before changing the validator
|
||||||
|
clear();
|
||||||
|
setPlaceholderText("");
|
||||||
|
|
||||||
|
type_ = type;
|
||||||
|
switch (type_) {
|
||||||
|
case kUInt64:
|
||||||
|
setValidator(&uint64Validator_);
|
||||||
|
if (isMaskMode_)
|
||||||
|
setText("0xFFFFFFFFFFFFFFFF");
|
||||||
|
break;
|
||||||
|
case kMacAddress:
|
||||||
|
setValidator(&macValidator_);
|
||||||
|
setPlaceholderText("00:00:00:00:00:00");
|
||||||
|
if (isMaskMode_)
|
||||||
|
setText("FF:FF:FF:FF:FF:FF");
|
||||||
|
break;
|
||||||
|
case kIp4Address:
|
||||||
|
setValidator(&ip4Validator_);
|
||||||
|
setPlaceholderText("0.0.0.0");
|
||||||
|
if (isMaskMode_)
|
||||||
|
setText("255.255.255.255");
|
||||||
|
break;
|
||||||
|
case kIp6Address:
|
||||||
|
setValidator(&ip6Validator_);
|
||||||
|
setPlaceholderText("::");
|
||||||
|
if (isMaskMode_)
|
||||||
|
setText("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setValidator(nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applicable only if type is kUInt64
|
||||||
|
void FieldEdit::setRange(quint64 min, quint64 max)
|
||||||
|
{
|
||||||
|
uint64Validator_.setRange(min, max);
|
||||||
|
if (type_ == kUInt64) {
|
||||||
|
setPlaceholderText(QString("%1 - %2").arg(min).arg(max));
|
||||||
|
if (isMaskMode_)
|
||||||
|
setText(QString::number(max, 16).toUpper().prepend("0x"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldEdit::setMaskMode(bool maskMode)
|
||||||
|
{
|
||||||
|
isMaskMode_ = maskMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FieldEdit::text() const
|
||||||
|
{
|
||||||
|
QString str = QLineEdit::text();
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
case kMacAddress:
|
||||||
|
str.remove(QRegularExpression("[:-]"));
|
||||||
|
str.prepend("0x");
|
||||||
|
break;
|
||||||
|
case kIp4Address:
|
||||||
|
str = QString::number(QHostAddress(str).toIPv4Address());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
60
client/fieldedit.h
Normal file
60
client/fieldedit.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _FIELD_EDIT_H
|
||||||
|
#define _FIELD_EDIT_H
|
||||||
|
|
||||||
|
#include "ipv4addressvalidator.h"
|
||||||
|
#include "ipv6addressvalidator.h"
|
||||||
|
#include "macaddressvalidator.h"
|
||||||
|
#include "ulonglongvalidator.h"
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class FieldEdit: public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum FieldType {
|
||||||
|
kUInt64,
|
||||||
|
kMacAddress,
|
||||||
|
kIp4Address,
|
||||||
|
kIp6Address
|
||||||
|
};
|
||||||
|
FieldEdit(QWidget *parent = 0);
|
||||||
|
|
||||||
|
void setType(FieldType type);
|
||||||
|
void setRange(quint64 min, quint64 max);
|
||||||
|
|
||||||
|
void setMaskMode(bool maskMode);
|
||||||
|
|
||||||
|
QString text() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldType type_{kUInt64};
|
||||||
|
bool isMaskMode_{false};
|
||||||
|
|
||||||
|
IPv4AddressValidator ip4Validator_;
|
||||||
|
IPv6AddressValidator ip6Validator_;
|
||||||
|
MacAddressValidator macValidator_;
|
||||||
|
ULongLongValidator uint64Validator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
230
client/findreplace.cpp
Normal file
230
client/findreplace.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "findreplace.h"
|
||||||
|
|
||||||
|
#include "abstractprotocol.h"
|
||||||
|
#include "iputils.h"
|
||||||
|
#include "mandatoryfieldsgroup.h"
|
||||||
|
#include "protocolmanager.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "uint128.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
extern ProtocolManager *OstProtocolManager;
|
||||||
|
|
||||||
|
// TODO: It might be useful for this dialog to support a "preview"
|
||||||
|
// of the replacements
|
||||||
|
|
||||||
|
FindReplaceDialog::FindReplaceDialog(Action *action, QWidget *parent)
|
||||||
|
: QDialog(parent), action_(action)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
findMask->setMaskMode(true);
|
||||||
|
replaceMask->setMaskMode(true);
|
||||||
|
|
||||||
|
// Keep things simple and don't use mask(s) (default)
|
||||||
|
useFindMask->setChecked(false);
|
||||||
|
useReplaceMask->setChecked(false);
|
||||||
|
|
||||||
|
// TODO: remove combo protocols - see note in StreamBase::findReplace
|
||||||
|
QStringList protocolList = OstProtocolManager->protocolDatabase();
|
||||||
|
protocolList.sort();
|
||||||
|
protocol->addItems(protocolList);
|
||||||
|
|
||||||
|
// Enable this setting if we have streams selected on input
|
||||||
|
selectedStreamsOnly->setEnabled(action->selectedStreamsOnly);
|
||||||
|
|
||||||
|
// Reset for user input
|
||||||
|
action->selectedStreamsOnly = false;
|
||||||
|
|
||||||
|
QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok);
|
||||||
|
ok->setText(tr("Replace All"));
|
||||||
|
ok->setDisabled(true);
|
||||||
|
|
||||||
|
mandatoryFields_ = new MandatoryFieldsGroup(this);
|
||||||
|
mandatoryFields_->add(protocol);
|
||||||
|
mandatoryFields_->add(field);
|
||||||
|
mandatoryFields_->add(findValue);
|
||||||
|
mandatoryFields_->add(findMask);
|
||||||
|
mandatoryFields_->add(replaceValue);
|
||||||
|
mandatoryFields_->add(replaceMask);
|
||||||
|
mandatoryFields_->setSubmitButton(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
FindReplaceDialog::~FindReplaceDialog()
|
||||||
|
{
|
||||||
|
delete mandatoryFields_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindReplaceDialog::on_protocol_currentIndexChanged(const QString &name)
|
||||||
|
{
|
||||||
|
field->clear();
|
||||||
|
fieldAttrib_.clear();
|
||||||
|
|
||||||
|
Stream stream;
|
||||||
|
AbstractProtocol *protocol = OstProtocolManager->createProtocol(
|
||||||
|
name, &stream);
|
||||||
|
int count = protocol->fieldCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// XXX: It might be useful to support meta fields too, later!
|
||||||
|
if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
||||||
|
.toInt();
|
||||||
|
if (bitSize <= 0) // skip optional fields
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FieldAttrib fieldAttrib;
|
||||||
|
fieldAttrib.index = i; // fieldIndex
|
||||||
|
fieldAttrib.bitSize = bitSize;
|
||||||
|
|
||||||
|
// field and fieldAttrib_ have same count and order of fields
|
||||||
|
fieldAttrib_.append(fieldAttrib);
|
||||||
|
field->addItem(protocol->fieldData(i, AbstractProtocol::FieldName)
|
||||||
|
.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protocolId_ = protocol->protocolNumber();
|
||||||
|
delete protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindReplaceDialog::on_field_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString fieldName = field->currentText();
|
||||||
|
FieldAttrib fieldAttrib = fieldAttrib_.at(index);
|
||||||
|
|
||||||
|
// Use heuristics to determine field type
|
||||||
|
if (fieldAttrib.bitSize == 48) {
|
||||||
|
findMask->setType(FieldEdit::kMacAddress);
|
||||||
|
findValue->setType(FieldEdit::kMacAddress);
|
||||||
|
replaceMask->setType(FieldEdit::kMacAddress);
|
||||||
|
replaceValue->setType(FieldEdit::kMacAddress);
|
||||||
|
} else if ((fieldAttrib.bitSize == 32)
|
||||||
|
&& (fieldName.contains(QRegularExpression(
|
||||||
|
"address|source|destination",
|
||||||
|
QRegularExpression::CaseInsensitiveOption)))) {
|
||||||
|
findMask->setType(FieldEdit::kIp4Address);
|
||||||
|
findValue->setType(FieldEdit::kIp4Address);
|
||||||
|
replaceMask->setType(FieldEdit::kIp4Address);
|
||||||
|
replaceValue->setType(FieldEdit::kIp4Address);
|
||||||
|
} else if ((fieldAttrib.bitSize == 128)
|
||||||
|
&& (fieldName.contains(QRegularExpression(
|
||||||
|
"address|source|destination",
|
||||||
|
QRegularExpression::CaseInsensitiveOption)))) {
|
||||||
|
findMask->setType(FieldEdit::kIp6Address);
|
||||||
|
findValue->setType(FieldEdit::kIp6Address);
|
||||||
|
replaceMask->setType(FieldEdit::kIp6Address);
|
||||||
|
replaceValue->setType(FieldEdit::kIp6Address);
|
||||||
|
} else {
|
||||||
|
quint64 max = quint64(~0) >> (64-fieldAttrib.bitSize);
|
||||||
|
qDebug("XXXXXX %s bitSize %d max %llx",
|
||||||
|
qPrintable(field->currentText()),
|
||||||
|
fieldAttrib.bitSize, max);
|
||||||
|
|
||||||
|
findMask->setType(FieldEdit::kUInt64);
|
||||||
|
findMask->setRange(0, max);
|
||||||
|
findValue->setType(FieldEdit::kUInt64);
|
||||||
|
findValue->setRange(0, max);
|
||||||
|
|
||||||
|
replaceMask->setType(FieldEdit::kUInt64);
|
||||||
|
replaceMask->setRange(0, max);
|
||||||
|
replaceValue->setType(FieldEdit::kUInt64);
|
||||||
|
replaceValue->setRange(0, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindReplaceDialog::on_matchAny_toggled(bool checked)
|
||||||
|
{
|
||||||
|
if (checked) {
|
||||||
|
findValueLabel->setHidden(true);
|
||||||
|
findValue->setHidden(true);
|
||||||
|
useFindMask->setHidden(true);
|
||||||
|
findMask->setHidden(true);
|
||||||
|
findMaskHint->setHidden(true);
|
||||||
|
} else {
|
||||||
|
findValueLabel->setVisible(true);
|
||||||
|
findValue->setVisible(true);
|
||||||
|
useFindMask->setVisible(true);
|
||||||
|
if (useFindMask->isChecked()) {
|
||||||
|
findMask->setVisible(true);
|
||||||
|
findMaskHint->setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindReplaceDialog::on_buttonBox_accepted()
|
||||||
|
{
|
||||||
|
FieldAttrib fieldAttrib = fieldAttrib_.at(field->currentIndex());
|
||||||
|
action_->protocolField = QString("%1 %2")
|
||||||
|
.arg(protocol->currentText())
|
||||||
|
.arg(field->currentText());
|
||||||
|
action_->protocolNumber = protocolId_;
|
||||||
|
action_->fieldIndex = fieldAttrib.index;
|
||||||
|
action_->fieldBitSize = fieldAttrib.bitSize;
|
||||||
|
|
||||||
|
if (fieldAttrib.bitSize == 128) { // IPv6 address
|
||||||
|
if (matchAny->isChecked()) {
|
||||||
|
action_->findMask.setValue(UInt128(0));
|
||||||
|
action_->findValue.setValue(UInt128(0));
|
||||||
|
} else {
|
||||||
|
action_->findMask.setValue(
|
||||||
|
useFindMask->isChecked() ?
|
||||||
|
ipUtils::ip6StringToUInt128(findMask->text()) :
|
||||||
|
~UInt128(0));
|
||||||
|
action_->findValue.setValue(
|
||||||
|
ipUtils::ip6StringToUInt128(findValue->text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
action_->replaceMask.setValue(
|
||||||
|
useReplaceMask->isChecked() ?
|
||||||
|
ipUtils::ip6StringToUInt128(replaceMask->text()) :
|
||||||
|
~UInt128(0));
|
||||||
|
action_->replaceValue.setValue(
|
||||||
|
ipUtils::ip6StringToUInt128(replaceValue->text()));
|
||||||
|
} else { // everything else
|
||||||
|
if (matchAny->isChecked()) {
|
||||||
|
action_->findMask.setValue(0);
|
||||||
|
action_->findValue.setValue(0);
|
||||||
|
} else {
|
||||||
|
action_->findMask.setValue(
|
||||||
|
useFindMask->isChecked() ?
|
||||||
|
findMask->text().toULongLong(nullptr, 0) :
|
||||||
|
~quint64(0));
|
||||||
|
action_->findValue.setValue(
|
||||||
|
findValue->text().toULongLong(nullptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
action_->replaceMask.setValue(
|
||||||
|
useReplaceMask->isChecked() ?
|
||||||
|
replaceMask->text().toULongLong(nullptr, 0) :
|
||||||
|
~quint64(0));
|
||||||
|
action_->replaceValue.setValue(QString::number(
|
||||||
|
replaceValue->text().toULongLong(nullptr, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
action_->selectedStreamsOnly = selectedStreamsOnly->isChecked();
|
||||||
|
}
|
||||||
|
|
71
client/findreplace.h
Normal file
71
client/findreplace.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _FIND_REPLACE_H
|
||||||
|
#define _FIND_REPLACE_H
|
||||||
|
|
||||||
|
#include "ui_findreplace.h"
|
||||||
|
|
||||||
|
class MandatoryFieldsGroup;
|
||||||
|
|
||||||
|
class FindReplaceDialog: public QDialog, public Ui::FindReplace
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
struct Action;
|
||||||
|
|
||||||
|
FindReplaceDialog(Action *action, QWidget *parent = 0);
|
||||||
|
~FindReplaceDialog();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_protocol_currentIndexChanged(const QString &name);
|
||||||
|
void on_field_currentIndexChanged(int index);
|
||||||
|
void on_matchAny_toggled(bool checked);
|
||||||
|
void on_buttonBox_accepted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct FieldAttrib;
|
||||||
|
|
||||||
|
quint32 protocolId_{0};
|
||||||
|
Action *action_{nullptr};
|
||||||
|
QList<FieldAttrib> fieldAttrib_;
|
||||||
|
MandatoryFieldsGroup *mandatoryFields_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FindReplaceDialog::Action
|
||||||
|
{
|
||||||
|
QString protocolField;
|
||||||
|
quint32 protocolNumber;
|
||||||
|
quint32 fieldIndex;
|
||||||
|
int fieldBitSize;
|
||||||
|
QVariant findValue;
|
||||||
|
QVariant findMask;
|
||||||
|
QVariant replaceValue;
|
||||||
|
QVariant replaceMask;
|
||||||
|
|
||||||
|
bool selectedStreamsOnly; // in-out param
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FindReplaceDialog::FieldAttrib
|
||||||
|
{
|
||||||
|
quint32 index;
|
||||||
|
int bitSize;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
265
client/findreplace.ui
Normal file
265
client/findreplace.ui
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FindReplace</class>
|
||||||
|
<widget class="QDialog" name="FindReplace">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>361</width>
|
||||||
|
<height>309</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Find & Replace</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="find">
|
||||||
|
<property name="title">
|
||||||
|
<string>Find</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="protocolLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Protocol</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>protocol</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="protocol"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="fieldLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Field</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>field</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="field"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="findValueLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="FieldEdit" name="findValue"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="useFindMask">
|
||||||
|
<property name="text">
|
||||||
|
<string>Mask</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="FieldEdit" name="findMask"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QLabel" name="findMaskHint">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p align="center">Matches a field only if <span style="white-space:nowrap">(FieldValue &amp; FindMask) = FindValue</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="matchAny">
|
||||||
|
<property name="text">
|
||||||
|
<string>Match any value</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="replace">
|
||||||
|
<property name="title">
|
||||||
|
<string>Replace with</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="replaceValueLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="FieldEdit" name="replaceValue"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="useReplaceMask">
|
||||||
|
<property name="text">
|
||||||
|
<string>Mask</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="FieldEdit" name="replaceMask"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel" name="replaceMaskHint">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p align="center">New field value = <span style="white-space:nowrap">(OldFieldValue &amp; ~ReplaceMask) | (ReplaceValue &amp; ReplaceMask)</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="selectedStreamsOnly">
|
||||||
|
<property name="text">
|
||||||
|
<string>Selected Streams Only</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>FieldEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>fieldedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>FindReplace</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>277</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>FindReplace</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>283</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>useFindMask</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>findMask</receiver>
|
||||||
|
<slot>setVisible(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>60</x>
|
||||||
|
<y>115</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>76</x>
|
||||||
|
<y>119</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>useReplaceMask</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>replaceMask</receiver>
|
||||||
|
<slot>setVisible(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>56</x>
|
||||||
|
<y>228</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>73</x>
|
||||||
|
<y>227</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>useReplaceMask</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>replaceMaskHint</receiver>
|
||||||
|
<slot>setVisible(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>50</x>
|
||||||
|
<y>230</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>333</x>
|
||||||
|
<y>233</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>useFindMask</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>findMaskHint</receiver>
|
||||||
|
<slot>setVisible(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>46</x>
|
||||||
|
<y>116</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>335</x>
|
||||||
|
<y>122</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
BIN
client/icons/find.png
Normal file
BIN
client/icons/find.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 659 B |
BIN
client/icons/info.png
Normal file
BIN
client/icons/info.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 778 B |
@ -182,6 +182,8 @@ void LogsWindow::alert(State state)
|
|||||||
// start - center of main window
|
// start - center of main window
|
||||||
QRect start;
|
QRect start;
|
||||||
QWidget *view = mainWindow;
|
QWidget *view = mainWindow;
|
||||||
|
if (!view)
|
||||||
|
return;
|
||||||
alert_->setParent(view);
|
alert_->setParent(view);
|
||||||
alert_->raise();
|
alert_->raise();
|
||||||
start.setSize(QSize(256, 256).scaled(view->size()/2, Qt::KeepAspectRatio));
|
start.setSize(QSize(256, 256).scaled(view->size()/2, Qt::KeepAspectRatio));
|
||||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "params.h"
|
#include "params.h"
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "thememanager.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@ -84,6 +85,8 @@ int main(int argc, char* argv[])
|
|||||||
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||||
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
ThemeManager::instance()->setTheme(appSettings->value(kThemeKey).toString());
|
||||||
|
|
||||||
qsrand(QDateTime::currentDateTime().toTime_t());
|
qsrand(QDateTime::currentDateTime().toTime_t());
|
||||||
|
|
||||||
mainWindow = new MainWindow;
|
mainWindow = new MainWindow;
|
||||||
|
@ -119,8 +119,11 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions());
|
menuFile->insertActions(menuFile->actions().at(3),
|
||||||
|
portsWindow->portActions());
|
||||||
menuEdit->addActions(clipboardHelper->actions());
|
menuEdit->addActions(clipboardHelper->actions());
|
||||||
|
menuStreams->addActions(portsWindow->streamActions());
|
||||||
|
menuDevices->addActions(portsWindow->deviceActions());
|
||||||
|
|
||||||
statsDock->setWidget(statsWindow);
|
statsDock->setWidget(statsWindow);
|
||||||
addDockWidget(Qt::BottomDockWidgetArea, statsDock);
|
addDockWidget(Qt::BottomDockWidgetArea, statsDock);
|
||||||
|
@ -25,21 +25,10 @@
|
|||||||
<addaction name="actionOpenSession" />
|
<addaction name="actionOpenSession" />
|
||||||
<addaction name="actionSaveSession" />
|
<addaction name="actionSaveSession" />
|
||||||
<addaction name="separator" />
|
<addaction name="separator" />
|
||||||
|
<addaction name="separator" />
|
||||||
<addaction name="actionPreferences" />
|
<addaction name="actionPreferences" />
|
||||||
<addaction name="actionFileExit" />
|
<addaction name="actionFileExit" />
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuHelp" >
|
|
||||||
<property name="title" >
|
|
||||||
<string>&Help</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionHelpOnline" />
|
|
||||||
<addaction name="separator" />
|
|
||||||
<addaction name="actionDonate" />
|
|
||||||
<addaction name="actionCheckForUpdates" />
|
|
||||||
<addaction name="separator" />
|
|
||||||
<addaction name="actionHelpAbout" />
|
|
||||||
<addaction name="actionAboutQt" />
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenu" name="menuEdit" >
|
<widget class="QMenu" name="menuEdit" >
|
||||||
<property name="title" >
|
<property name="title" >
|
||||||
<string>&Edit</string>
|
<string>&Edit</string>
|
||||||
@ -52,9 +41,34 @@
|
|||||||
<addaction name="actionViewShowMyReservedPortsOnly" />
|
<addaction name="actionViewShowMyReservedPortsOnly" />
|
||||||
<addaction name="actionViewRestoreDefaults" />
|
<addaction name="actionViewRestoreDefaults" />
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuStreams" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>&Streams</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionTest" />
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuDevices" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>&Devices</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuHelp" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>&Help</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionHelpOnline" />
|
||||||
|
<addaction name="separator" />
|
||||||
|
<addaction name="actionDonate" />
|
||||||
|
<addaction name="actionCheckForUpdates" />
|
||||||
|
<addaction name="separator" />
|
||||||
|
<addaction name="actionHelpAbout" />
|
||||||
|
<addaction name="actionAboutQt" />
|
||||||
|
</widget>
|
||||||
<addaction name="menuFile" />
|
<addaction name="menuFile" />
|
||||||
<addaction name="menuEdit" />
|
<addaction name="menuEdit" />
|
||||||
<addaction name="menuView" />
|
<addaction name="menuView" />
|
||||||
|
<addaction name="menuStreams" />
|
||||||
|
<addaction name="menuDevices" />
|
||||||
<addaction name="menuHelp" />
|
<addaction name="menuHelp" />
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusbar" />
|
<widget class="QStatusBar" name="statusbar" />
|
||||||
|
126
client/mandatoryfieldsgroup.cpp
Normal file
126
client/mandatoryfieldsgroup.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "mandatoryfieldsgroup.h"
|
||||||
|
|
||||||
|
// No need for QDateEdit, QSpinBox, etc., since these always return values
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
void MandatoryFieldsGroup::add(QWidget *widget)
|
||||||
|
{
|
||||||
|
if (!widgets_.contains(widget)) {
|
||||||
|
if (widget->inherits("QCheckBox"))
|
||||||
|
connect(qobject_cast<QCheckBox*>(widget),
|
||||||
|
SIGNAL(clicked()),
|
||||||
|
this, SLOT(changed()));
|
||||||
|
else if (widget->inherits("QComboBox"))
|
||||||
|
connect(qobject_cast<QComboBox*>(widget),
|
||||||
|
SIGNAL(highlighted(int)),
|
||||||
|
this, SLOT(changed()));
|
||||||
|
else if (widget->inherits("QLineEdit"))
|
||||||
|
connect(qobject_cast<QLineEdit*>(widget),
|
||||||
|
SIGNAL(textChanged(const QString&)),
|
||||||
|
this, SLOT(changed()));
|
||||||
|
else {
|
||||||
|
qWarning("MandatoryFieldsGroup: unsupported class %s",
|
||||||
|
widget->metaObject()->className());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets_.append(widget);
|
||||||
|
changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MandatoryFieldsGroup::remove(QWidget *widget)
|
||||||
|
{
|
||||||
|
widgets_.removeAll(widget);
|
||||||
|
|
||||||
|
changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MandatoryFieldsGroup::setSubmitButton(QPushButton *button)
|
||||||
|
{
|
||||||
|
if (submitButton_ && submitButton_ != button)
|
||||||
|
submitButton_->setEnabled(true);
|
||||||
|
submitButton_ = button;
|
||||||
|
|
||||||
|
changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MandatoryFieldsGroup::changed()
|
||||||
|
{
|
||||||
|
if (!submitButton_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool enable = true;
|
||||||
|
for (auto widget : widgets_) {
|
||||||
|
// Invisible mandatory widgets are treated as non-mandatory
|
||||||
|
if (!widget->isVisible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (widget->inherits("QCheckBox")) {
|
||||||
|
// Makes sense only for tristate checkbox
|
||||||
|
auto checkBox = qobject_cast<const QCheckBox*>(widget);
|
||||||
|
if (checkBox->checkState() == Qt::PartiallyChecked) {
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget->inherits("QComboBox")) {
|
||||||
|
auto comboBox = qobject_cast<const QComboBox*>(widget);
|
||||||
|
if (comboBox->currentText().isEmpty()) {
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget->inherits("QLineEdit")) {
|
||||||
|
auto lineEdit = qobject_cast<const QLineEdit*>(widget);
|
||||||
|
if (lineEdit->text().isEmpty()
|
||||||
|
|| !lineEdit->hasAcceptableInput()) {
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitButton_->setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MandatoryFieldsGroup::clear()
|
||||||
|
{
|
||||||
|
widgets_.clear();
|
||||||
|
|
||||||
|
if (submitButton_) {
|
||||||
|
submitButton_->setEnabled(true);
|
||||||
|
submitButton_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
59
client/mandatoryfieldsgroup.h
Normal file
59
client/mandatoryfieldsgroup.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _MANDATORY_FIELDS_GROUP_H
|
||||||
|
#define _MANDATORY_FIELDS_GROUP_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
// Adapted from https://doc.qt.io/archives/qq/qq11-mandatoryfields.html
|
||||||
|
// and improved
|
||||||
|
|
||||||
|
class MandatoryFieldsGroup : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MandatoryFieldsGroup(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(QWidget *widget);
|
||||||
|
void remove(QWidget *widget);
|
||||||
|
void setSubmitButton(QPushButton *button);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<const QWidget*> widgets_;
|
||||||
|
QPushButton *submitButton_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -41,10 +41,13 @@ HEADERS += \
|
|||||||
devicemodel.h \
|
devicemodel.h \
|
||||||
deviceswidget.h \
|
deviceswidget.h \
|
||||||
dumpview.h \
|
dumpview.h \
|
||||||
|
fieldedit.h \
|
||||||
hexlineedit.h \
|
hexlineedit.h \
|
||||||
logsmodel.h \
|
logsmodel.h \
|
||||||
logswindow.h \
|
logswindow.h \
|
||||||
|
findreplace.h \
|
||||||
mainwindow.h \
|
mainwindow.h \
|
||||||
|
mandatoryfieldsgroup.h \
|
||||||
ndpstatusmodel.h \
|
ndpstatusmodel.h \
|
||||||
packetmodel.h \
|
packetmodel.h \
|
||||||
port.h \
|
port.h \
|
||||||
@ -57,6 +60,7 @@ HEADERS += \
|
|||||||
portstatsproxymodel.h \
|
portstatsproxymodel.h \
|
||||||
portstatswindow.h \
|
portstatswindow.h \
|
||||||
portswindow.h \
|
portswindow.h \
|
||||||
|
portwidget.h \
|
||||||
preferences.h \
|
preferences.h \
|
||||||
settings.h \
|
settings.h \
|
||||||
streamconfigdialog.h \
|
streamconfigdialog.h \
|
||||||
@ -65,6 +69,7 @@ HEADERS += \
|
|||||||
streamstatsfiltermodel.h \
|
streamstatsfiltermodel.h \
|
||||||
streamstatsmodel.h \
|
streamstatsmodel.h \
|
||||||
streamstatswindow.h \
|
streamstatswindow.h \
|
||||||
|
streamswidget.h \
|
||||||
variablefieldswidget.h \
|
variablefieldswidget.h \
|
||||||
xtableview.h
|
xtableview.h
|
||||||
|
|
||||||
@ -72,15 +77,18 @@ FORMS += \
|
|||||||
about.ui \
|
about.ui \
|
||||||
devicegroupdialog.ui \
|
devicegroupdialog.ui \
|
||||||
deviceswidget.ui \
|
deviceswidget.ui \
|
||||||
|
findreplace.ui \
|
||||||
logswindow.ui \
|
logswindow.ui \
|
||||||
mainwindow.ui \
|
mainwindow.ui \
|
||||||
portconfigdialog.ui \
|
portconfigdialog.ui \
|
||||||
portstatsfilter.ui \
|
portstatsfilter.ui \
|
||||||
portstatswindow.ui \
|
portstatswindow.ui \
|
||||||
portswindow.ui \
|
portswindow.ui \
|
||||||
|
portwidget.ui \
|
||||||
preferences.ui \
|
preferences.ui \
|
||||||
streamconfigdialog.ui \
|
streamconfigdialog.ui \
|
||||||
streamstatswindow.ui \
|
streamstatswindow.ui \
|
||||||
|
streamswidget.ui \
|
||||||
variablefieldswidget.ui
|
variablefieldswidget.ui
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@ -95,8 +103,11 @@ SOURCES += \
|
|||||||
hexlineedit.cpp \
|
hexlineedit.cpp \
|
||||||
logsmodel.cpp \
|
logsmodel.cpp \
|
||||||
logswindow.cpp \
|
logswindow.cpp \
|
||||||
|
fieldedit.cpp \
|
||||||
|
findreplace.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
mainwindow.cpp \
|
mainwindow.cpp \
|
||||||
|
mandatoryfieldsgroup.cpp \
|
||||||
ndpstatusmodel.cpp \
|
ndpstatusmodel.cpp \
|
||||||
packetmodel.cpp \
|
packetmodel.cpp \
|
||||||
params.cpp \
|
params.cpp \
|
||||||
@ -109,18 +120,31 @@ SOURCES += \
|
|||||||
portstatsfilterdialog.cpp \
|
portstatsfilterdialog.cpp \
|
||||||
portstatswindow.cpp \
|
portstatswindow.cpp \
|
||||||
portswindow.cpp \
|
portswindow.cpp \
|
||||||
|
portwidget.cpp \
|
||||||
preferences.cpp \
|
preferences.cpp \
|
||||||
streamconfigdialog.cpp \
|
streamconfigdialog.cpp \
|
||||||
streamlistdelegate.cpp \
|
streamlistdelegate.cpp \
|
||||||
streammodel.cpp \
|
streammodel.cpp \
|
||||||
streamstatsmodel.cpp \
|
streamstatsmodel.cpp \
|
||||||
streamstatswindow.cpp \
|
streamstatswindow.cpp \
|
||||||
|
streamswidget.cpp \
|
||||||
|
thememanager.cpp \
|
||||||
variablefieldswidget.cpp
|
variablefieldswidget.cpp
|
||||||
|
|
||||||
|
THEMES += \
|
||||||
|
themes/material-dark.qss \
|
||||||
|
themes/material-dark.rcc \
|
||||||
|
themes/material-light.qss \
|
||||||
|
themes/material-light.rcc \
|
||||||
|
themes/qds-dark.qss \
|
||||||
|
themes/qds-dark.rcc \
|
||||||
|
themes/qds-light.qss \
|
||||||
|
themes/qds-light.rcc \
|
||||||
|
|
||||||
QMAKE_DISTCLEAN += object_script.*
|
QMAKE_DISTCLEAN += object_script.*
|
||||||
|
|
||||||
include(../install.pri)
|
include(../install.pri)
|
||||||
|
include(../shared.pri)
|
||||||
include(../version.pri)
|
include(../version.pri)
|
||||||
include(../options.pri)
|
include(../options.pri)
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
|
<file>icons/find.png</file>
|
||||||
|
<file>icons/info.png</file>
|
||||||
<file>icons/about.png</file>
|
<file>icons/about.png</file>
|
||||||
<file>icons/add.png</file>
|
<file>icons/add.png</file>
|
||||||
<file>icons/anime_error.gif</file>
|
<file>icons/anime_error.gif</file>
|
||||||
|
@ -88,7 +88,10 @@ void Port::updatePortConfig(OstProto::Port *port)
|
|||||||
setAlias(QString("if%1").arg(id()));
|
setAlias(QString("if%1").arg(id()));
|
||||||
|
|
||||||
if (recalc)
|
if (recalc)
|
||||||
|
{
|
||||||
recalculateAverageRates();
|
recalculateAverageRates();
|
||||||
|
emit streamListChanged(mPortGroupId, mPortId); // show/hide 'next' col
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Port::updateStreamOrdinalsFromIndex()
|
void Port::updateStreamOrdinalsFromIndex()
|
||||||
@ -314,6 +317,12 @@ void Port::setAverageBitRate(double bitsPerSec)
|
|||||||
emit portRateChanged(mPortGroupId, mPortId);
|
emit portRateChanged(mPortGroupId, mPortId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Port::setAverageLoadRate(double load)
|
||||||
|
{
|
||||||
|
Q_ASSERT(d.speed() > 0);
|
||||||
|
setAverageBitRate(load*d.speed()*1e6);
|
||||||
|
}
|
||||||
|
|
||||||
bool Port::newStreamAt(int index, OstProto::Stream const *stream)
|
bool Port::newStreamAt(int index, OstProto::Stream const *stream)
|
||||||
{
|
{
|
||||||
Stream *s = new Stream;
|
Stream *s = new Stream;
|
||||||
@ -600,7 +609,6 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
|||||||
int ret;
|
int ret;
|
||||||
optDialog->setParent(mainWindow, Qt::Dialog);
|
optDialog->setParent(mainWindow, Qt::Dialog);
|
||||||
ret = optDialog->exec();
|
ret = optDialog->exec();
|
||||||
optDialog->setParent(0, Qt::Dialog);
|
|
||||||
if (ret == QDialog::Rejected)
|
if (ret == QDialog::Rejected)
|
||||||
goto _user_opt_cancel;
|
goto _user_opt_cancel;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,10 @@ public:
|
|||||||
{ return d.transmit_mode(); }
|
{ return d.transmit_mode(); }
|
||||||
bool trackStreamStats() const
|
bool trackStreamStats() const
|
||||||
{ return d.is_tracking_stream_stats(); }
|
{ return d.is_tracking_stream_stats(); }
|
||||||
|
double speed() const
|
||||||
|
{ return d.speed(); }
|
||||||
|
double averageLoadRate() const
|
||||||
|
{ return d.speed() ? avgBitsPerSec_/(d.speed()*1e6) : 0; }
|
||||||
double averagePacketRate() const
|
double averagePacketRate() const
|
||||||
{ return avgPacketsPerSec_; }
|
{ return avgPacketsPerSec_; }
|
||||||
double averageBitRate() const
|
double averageBitRate() const
|
||||||
@ -183,6 +187,7 @@ public:
|
|||||||
|
|
||||||
void setAveragePacketRate(double packetsPerSec);
|
void setAveragePacketRate(double packetsPerSec);
|
||||||
void setAverageBitRate(double bitsPerSec);
|
void setAverageBitRate(double bitsPerSec);
|
||||||
|
void setAverageLoadRate(double loadPercent);
|
||||||
// FIXME(MED): Bad Hack! port should not need an external trigger to
|
// FIXME(MED): Bad Hack! port should not need an external trigger to
|
||||||
// recalculate - refactor client side domain objects and model objects
|
// recalculate - refactor client side domain objects and model objects
|
||||||
void recalculateAverageRates();
|
void recalculateAverageRates();
|
||||||
|
@ -87,6 +87,7 @@ void PortConfigDialog::accept()
|
|||||||
else
|
else
|
||||||
Q_ASSERT(false); // Unreachable!!!
|
Q_ASSERT(false); // Unreachable!!!
|
||||||
|
|
||||||
|
pc.set_user_name(portConfig_.user_name());
|
||||||
switch (reservedBy_) {
|
switch (reservedBy_) {
|
||||||
case kSelf:
|
case kSelf:
|
||||||
if (!reserveButton->isChecked())
|
if (!reserveButton->isChecked())
|
||||||
|
@ -206,6 +206,7 @@ void PortGroup::processVersionCompatibility(PbRpcController *controller)
|
|||||||
logError(id(), QString("checkVersion failed: %1")
|
logError(id(), QString("checkVersion failed: %1")
|
||||||
.arg(QString::fromStdString(verCompat->notes())));
|
.arg(QString::fromStdString(verCompat->notes())));
|
||||||
compat = kIncompatible;
|
compat = kIncompatible;
|
||||||
|
reconnect = false;
|
||||||
emit portGroupDataChanged(mPortGroupId);
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
@ -253,6 +254,16 @@ void PortGroup::on_rpcChannel_disconnected()
|
|||||||
emit portListChanged(mPortGroupId);
|
emit portListChanged(mPortGroupId);
|
||||||
emit portGroupDataChanged(mPortGroupId);
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
|
// Disconnected during apply? Restore UI.
|
||||||
|
if (applyTimer_.isValid()) {
|
||||||
|
applyTimer_.invalidate();
|
||||||
|
|
||||||
|
emit applyFinished();
|
||||||
|
|
||||||
|
mainWindow->setEnabled(true);
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
}
|
||||||
|
|
||||||
isGetStatsPending_ = false;
|
isGetStatsPending_ = false;
|
||||||
|
|
||||||
if (reconnect)
|
if (reconnect)
|
||||||
@ -279,8 +290,6 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError)
|
|||||||
" - is it drone or some other process?")
|
" - is it drone or some other process?")
|
||||||
.arg(rpcChannel->serverName())
|
.arg(rpcChannel->serverName())
|
||||||
.arg(rpcChannel->serverPort()));
|
.arg(rpcChannel->serverPort()));
|
||||||
// fall-through
|
|
||||||
case QAbstractSocket::RemoteHostClosedError:
|
|
||||||
reconnect = false;
|
reconnect = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -817,6 +826,7 @@ void PortGroup::processApplyBuildAck(int portIndex, PbRpcController *controller)
|
|||||||
logInfo(id(), mPorts[portIndex]->id(),
|
logInfo(id(), mPorts[portIndex]->id(),
|
||||||
QString("All port changes applied - in %1s")
|
QString("All port changes applied - in %1s")
|
||||||
.arg(applyTimer_.elapsed()/1e3));
|
.arg(applyTimer_.elapsed()/1e3));
|
||||||
|
applyTimer_.invalidate();
|
||||||
|
|
||||||
if (controller->Failed())
|
if (controller->Failed())
|
||||||
{
|
{
|
||||||
@ -1101,8 +1111,11 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
|||||||
// * modify (new) deviceGroups
|
// * modify (new) deviceGroups
|
||||||
// * add (new) stream ids
|
// * add (new) stream ids
|
||||||
// * modify (new) streams
|
// * modify (new) streams
|
||||||
|
// * resolve neighbors
|
||||||
|
// * build packets
|
||||||
// XXX: This assumes getDeviceGroupIdList() was invoked before
|
// XXX: This assumes getDeviceGroupIdList() was invoked before
|
||||||
// getStreamIdList() - if the order changes this code will break!
|
// getStreamIdList() - if the order changes this code will break!
|
||||||
|
// XXX: See resolve/build notes below
|
||||||
|
|
||||||
// XXX: same name as input param, but shouldn't cause any problem
|
// XXX: same name as input param, but shouldn't cause any problem
|
||||||
PbRpcController *controller;
|
PbRpcController *controller;
|
||||||
@ -1143,6 +1156,7 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add/modify deviceGroups
|
// add/modify deviceGroups
|
||||||
|
bool resolve = false;
|
||||||
if (newPortContent->device_groups_size())
|
if (newPortContent->device_groups_size())
|
||||||
{
|
{
|
||||||
OstProto::DeviceGroupIdList *deviceGroupIdList
|
OstProto::DeviceGroupIdList *deviceGroupIdList
|
||||||
@ -1174,6 +1188,7 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
|||||||
deviceGroupConfigList, ack,
|
deviceGroupConfigList, ack,
|
||||||
NewCallback(this, &PortGroup::processModifyDeviceGroupAck,
|
NewCallback(this, &PortGroup::processModifyDeviceGroupAck,
|
||||||
portIndex, controller));
|
portIndex, controller));
|
||||||
|
resolve = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add/modify streams
|
// add/modify streams
|
||||||
@ -1204,6 +1219,26 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
|||||||
serviceStub->modifyStream(controller, streamConfigList, ack,
|
serviceStub->modifyStream(controller, streamConfigList, ack,
|
||||||
NewCallback(this, &PortGroup::processModifyStreamAck,
|
NewCallback(this, &PortGroup::processModifyStreamAck,
|
||||||
portIndex, controller));
|
portIndex, controller));
|
||||||
|
resolve = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: Ideally resolve and build should be called after **all**
|
||||||
|
// ports and portgroups are configured. As of now, any resolve
|
||||||
|
// replied to by ports/portgroups configured later in the open
|
||||||
|
// session sequence will fail.
|
||||||
|
// However, to do that, we may need to rethink the open session
|
||||||
|
// implementation - so going with this for now
|
||||||
|
if (resolve)
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
portIdList->add_port_id()->set_id(portId);
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
controller = new PbRpcController(portIdList, ack);
|
||||||
|
serviceStub->resolveDeviceNeighbors(controller, portIdList, ack,
|
||||||
|
NewCallback(this,
|
||||||
|
&PortGroup::processResolveDeviceNeighborsAck,
|
||||||
|
controller));
|
||||||
|
resolve = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build packets using the new config
|
// build packets using the new config
|
||||||
@ -2017,9 +2052,12 @@ bool PortGroup::getStreamStats(QList<uint> *portList)
|
|||||||
if (portList == NULL)
|
if (portList == NULL)
|
||||||
guidList->mutable_port_id_list()->CopyFrom(*portIdList_);
|
guidList->mutable_port_id_list()->CopyFrom(*portIdList_);
|
||||||
else
|
else
|
||||||
for (int i = 0; i < portList->size(); i++)
|
for (int i = 0; i < portList->size(); i++) {
|
||||||
guidList->mutable_port_id_list()->add_port_id()
|
guidList->mutable_port_id_list()->add_port_id()
|
||||||
->set_id(portList->at(i));
|
->set_id(portList->at(i));
|
||||||
|
if (mPorts.at(i)->isTransmitting())
|
||||||
|
logWarn(id(), i, "Port is still transmitting - stream stats may be unavailable or incomplete");
|
||||||
|
}
|
||||||
|
|
||||||
serviceStub->getStreamStats(controller, guidList, statsList,
|
serviceStub->getStreamStats(controller, guidList, statsList,
|
||||||
NewCallback(this, &PortGroup::processStreamStatsList, controller));
|
NewCallback(this, &PortGroup::processStreamStatsList, controller));
|
||||||
|
@ -68,6 +68,7 @@ bool PortGroupList::isPort(const QModelIndex& index)
|
|||||||
|
|
||||||
PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(index.isValid());
|
||||||
Q_ASSERT(mPortGroupListModel.isPortGroup(index));
|
Q_ASSERT(mPortGroupListModel.isPortGroup(index));
|
||||||
|
|
||||||
return *(mPortGroups[index.row()]);
|
return *(mPortGroups[index.row()]);
|
||||||
@ -75,6 +76,8 @@ PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
|||||||
|
|
||||||
Port& PortGroupList::port(const QModelIndex& index)
|
Port& PortGroupList::port(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(index.isValid());
|
||||||
|
Q_ASSERT(index.parent().isValid());
|
||||||
Q_ASSERT(mPortGroupListModel.isPort(index));
|
Q_ASSERT(mPortGroupListModel.isPort(index));
|
||||||
return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]);
|
return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]);
|
||||||
}
|
}
|
||||||
@ -110,8 +113,10 @@ void PortGroupList::addPortGroup(PortGroup &portGroup)
|
|||||||
|
|
||||||
void PortGroupList::removePortGroup(PortGroup &portGroup)
|
void PortGroupList::removePortGroup(PortGroup &portGroup)
|
||||||
{
|
{
|
||||||
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
|
// Disconnect before removing from list
|
||||||
|
portGroup.disconnectFromHost();
|
||||||
|
|
||||||
|
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
|
||||||
PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup));
|
PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup));
|
||||||
qDebug("after takeAt()");
|
qDebug("after takeAt()");
|
||||||
mPortGroupListModel.portGroupRemoved();
|
mPortGroupListModel.portGroupRemoved();
|
||||||
@ -128,11 +133,12 @@ void PortGroupList::removeAllPortGroups()
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
PortGroup *pg = mPortGroups.at(0);
|
PortGroup *pg = mPortGroups.at(0);
|
||||||
|
pg->disconnectFromHost();
|
||||||
mPortGroupListModel.portGroupAboutToBeRemoved(pg);
|
mPortGroupListModel.portGroupAboutToBeRemoved(pg);
|
||||||
mPortGroups.removeFirst();
|
mPortGroups.removeFirst();
|
||||||
delete pg;
|
delete pg;
|
||||||
|
mPortGroupListModel.portGroupRemoved();
|
||||||
} while (!mPortGroups.isEmpty());
|
} while (!mPortGroups.isEmpty());
|
||||||
mPortGroupListModel.portGroupRemoved();
|
|
||||||
|
|
||||||
mPortGroupListModel.when_portListChanged();
|
mPortGroupListModel.when_portListChanged();
|
||||||
mPortStatsModel.when_portListChanged();
|
mPortStatsModel.when_portListChanged();
|
||||||
|
@ -331,9 +331,24 @@ void PortStatsWindow::on_tbGetStreamStats_clicked()
|
|||||||
QDockWidget *statsDock = mainWindow->findChild<QDockWidget*>(
|
QDockWidget *statsDock = mainWindow->findChild<QDockWidget*>(
|
||||||
"statsDock");
|
"statsDock");
|
||||||
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
||||||
|
|
||||||
|
// Add stream stats tab to the immediate right of port-stats ...
|
||||||
mainWindow->tabifyDockWidget(statsDock, dock);
|
mainWindow->tabifyDockWidget(statsDock, dock);
|
||||||
|
mainWindow->splitDockWidget(statsDock, dock, Qt::Horizontal);
|
||||||
|
|
||||||
|
// ... make it the currently visible tab ...
|
||||||
dock->show();
|
dock->show();
|
||||||
dock->raise();
|
dock->raise();
|
||||||
|
|
||||||
|
// ... and set tab remove behaviour
|
||||||
|
// XXX: unfortunately, there's no direct way to get the TabBar
|
||||||
|
QList<QTabBar*> tabBars = mainWindow->findChildren<QTabBar*>();
|
||||||
|
foreach(QTabBar* tabBar, tabBars) {
|
||||||
|
if (tabBar->tabText(tabBar->currentIndex())
|
||||||
|
== dock->widget()->windowTitle())
|
||||||
|
tabBar->setSelectionBehaviorOnRemove(
|
||||||
|
QTabBar::SelectPreviousTab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get stream stats for selected ports, portgroup by portgroup
|
// Get stream stats for selected ports, portgroup by portgroup
|
||||||
|
@ -165,7 +165,7 @@
|
|||||||
<string>Stop Capture</string>
|
<string>Stop Capture</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="statusTip">
|
<property name="statusTip">
|
||||||
<string>End capture on selecteed port(s)</string>
|
<string>End capture on selected port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Stop</string>
|
<string>Stop</string>
|
||||||
|
@ -20,51 +20,37 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "portswindow.h"
|
#include "portswindow.h"
|
||||||
|
|
||||||
#include "applymsg.h"
|
#include "applymsg.h"
|
||||||
#include "clipboardhelper.h"
|
|
||||||
#include "deviceswidget.h"
|
#include "deviceswidget.h"
|
||||||
#include "portconfigdialog.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "streamconfigdialog.h"
|
|
||||||
#include "streamfileformat.h"
|
|
||||||
#include "streamlistdelegate.h"
|
|
||||||
|
|
||||||
#include "fileformat.pb.h"
|
#include "fileformat.pb.h"
|
||||||
|
#include "portconfigdialog.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "portwidget.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "streamswidget.h"
|
||||||
|
|
||||||
#include "xqlocale.h"
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QItemSelectionModel>
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QProgressDialog>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
extern ClipboardHelper *clipboardHelper;
|
|
||||||
extern QMainWindow *mainWindow;
|
extern QMainWindow *mainWindow;
|
||||||
|
|
||||||
PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
||||||
: QWidget(parent), proxyPortModel(NULL)
|
: QWidget(parent), proxyPortModel(NULL)
|
||||||
{
|
{
|
||||||
QAction *sep;
|
|
||||||
|
|
||||||
delegate = new StreamListDelegate;
|
|
||||||
proxyPortModel = new QSortFilterProxyModel(this);
|
proxyPortModel = new QSortFilterProxyModel(this);
|
||||||
|
|
||||||
//slm = new StreamListModel();
|
|
||||||
//plm = new PortGroupList();
|
|
||||||
plm = pgl;
|
plm = pgl;
|
||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
applyMsg_ = new ApplyMessage();
|
applyMsg_ = new ApplyMessage();
|
||||||
|
portWidget->setPortGroupList(plm);
|
||||||
|
streamsWidget->setPortGroupList(plm);
|
||||||
devicesWidget->setPortGroupList(plm);
|
devicesWidget->setPortGroupList(plm);
|
||||||
|
|
||||||
tvPortList->header()->hide();
|
tvPortList->header()->hide();
|
||||||
|
|
||||||
tvStreamList->setItemDelegate(delegate);
|
|
||||||
|
|
||||||
tvStreamList->verticalHeader()->setDefaultSectionSize(
|
|
||||||
tvStreamList->verticalHeader()->minimumSectionSize());
|
|
||||||
|
|
||||||
// Populate PortList Context Menu Actions
|
// Populate PortList Context Menu Actions
|
||||||
tvPortList->addAction(actionNew_Port_Group);
|
tvPortList->addAction(actionNew_Port_Group);
|
||||||
tvPortList->addAction(actionDelete_Port_Group);
|
tvPortList->addAction(actionDelete_Port_Group);
|
||||||
@ -74,41 +60,18 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
|||||||
tvPortList->addAction(actionExclusive_Control);
|
tvPortList->addAction(actionExclusive_Control);
|
||||||
tvPortList->addAction(actionPort_Configuration);
|
tvPortList->addAction(actionPort_Configuration);
|
||||||
|
|
||||||
// Populate StreamList Context Menu Actions
|
|
||||||
tvStreamList->addAction(actionNew_Stream);
|
|
||||||
tvStreamList->addAction(actionEdit_Stream);
|
|
||||||
tvStreamList->addAction(actionDuplicate_Stream);
|
|
||||||
tvStreamList->addAction(actionDelete_Stream);
|
|
||||||
|
|
||||||
QAction *sep2 = new QAction(this);
|
|
||||||
sep2->setSeparator(true);
|
|
||||||
tvStreamList->addAction(sep2);
|
|
||||||
|
|
||||||
tvStreamList->addAction(actionOpen_Streams);
|
|
||||||
tvStreamList->addAction(actionSave_Streams);
|
|
||||||
|
|
||||||
// PortList, StreamList, DeviceWidget actions combined
|
// PortList, StreamList, DeviceWidget actions combined
|
||||||
// make this window's actions
|
// make this window's actions
|
||||||
addActions(tvPortList->actions());
|
addActions(tvPortList->actions());
|
||||||
sep = new QAction(this);
|
QAction *sep = new QAction(this);
|
||||||
sep->setSeparator(true);
|
sep->setSeparator(true);
|
||||||
addAction(sep);
|
addAction(sep);
|
||||||
addActions(tvStreamList->actions());
|
addActions(streamsWidget->actions());
|
||||||
sep = new QAction(this);
|
sep = new QAction(this);
|
||||||
sep->setSeparator(true);
|
sep->setSeparator(true);
|
||||||
addAction(sep);
|
addAction(sep);
|
||||||
addActions(devicesWidget->actions());
|
addActions(devicesWidget->actions());
|
||||||
|
|
||||||
// Add the clipboard actions to the context menu of streamList
|
|
||||||
// but not to PortsWindow's actions since they are already available
|
|
||||||
// in the global Edit Menu
|
|
||||||
sep = new QAction("Clipboard", this);
|
|
||||||
sep->setSeparator(true);
|
|
||||||
tvStreamList->insertAction(sep2, sep);
|
|
||||||
tvStreamList->insertActions(sep2, clipboardHelper->actions());
|
|
||||||
|
|
||||||
tvStreamList->setModel(plm->getStreamModel());
|
|
||||||
|
|
||||||
// XXX: It would be ideal if we only needed to do the below to
|
// XXX: It would be ideal if we only needed to do the below to
|
||||||
// get the proxy model to do its magic. However, the QModelIndex
|
// get the proxy model to do its magic. However, the QModelIndex
|
||||||
// used by the source model and the proxy model are different
|
// used by the source model and the proxy model are different
|
||||||
@ -138,52 +101,24 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
|||||||
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
this, SLOT(when_portView_currentChanged(const QModelIndex&,
|
this, SLOT(when_portView_currentChanged(const QModelIndex&,
|
||||||
const QModelIndex&)));
|
const QModelIndex&)));
|
||||||
|
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
portWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
||||||
|
connect(this,
|
||||||
|
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
streamsWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
||||||
connect(this,
|
connect(this,
|
||||||
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
devicesWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
devicesWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
||||||
|
|
||||||
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
|
|
||||||
SLOT(updateStreamViewActions()));
|
|
||||||
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
|
|
||||||
SLOT(updateStreamViewActions()));
|
|
||||||
|
|
||||||
connect(tvStreamList->selectionModel(),
|
|
||||||
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
|
||||||
SLOT(updateStreamViewActions()));
|
|
||||||
connect(tvStreamList->selectionModel(),
|
|
||||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
|
||||||
SLOT(updateStreamViewActions()));
|
|
||||||
|
|
||||||
tvStreamList->resizeColumnToContents(StreamModel::StreamIcon);
|
|
||||||
tvStreamList->resizeColumnToContents(StreamModel::StreamStatus);
|
|
||||||
|
|
||||||
// Initially we don't have any ports/streams/devices
|
// Initially we don't have any ports/streams/devices
|
||||||
// - so send signal triggers
|
// - so send signal triggers
|
||||||
when_portView_currentChanged(QModelIndex(), QModelIndex());
|
when_portView_currentChanged(QModelIndex(), QModelIndex());
|
||||||
updateStreamViewActions();
|
|
||||||
|
|
||||||
connect(plm->getStreamModel(),
|
|
||||||
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
|
||||||
this, SLOT(streamModelDataChanged()));
|
|
||||||
connect(plm->getStreamModel(),
|
|
||||||
SIGNAL(modelReset()),
|
|
||||||
this, SLOT(streamModelDataChanged()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::streamModelDataChanged()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
if (plm->isPort(current))
|
|
||||||
plm->port(current).recalculateAverageRates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PortsWindow::~PortsWindow()
|
PortsWindow::~PortsWindow()
|
||||||
{
|
{
|
||||||
delete delegate;
|
|
||||||
delete proxyPortModel;
|
delete proxyPortModel;
|
||||||
delete applyMsg_;
|
delete applyMsg_;
|
||||||
}
|
}
|
||||||
@ -298,6 +233,21 @@ bool PortsWindow::saveSession(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QAction*> PortsWindow::portActions()
|
||||||
|
{
|
||||||
|
return tvPortList->actions();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction*> PortsWindow::streamActions()
|
||||||
|
{
|
||||||
|
return streamsWidget->actions();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction*> PortsWindow::deviceActions()
|
||||||
|
{
|
||||||
|
return devicesWidget->actions();
|
||||||
|
}
|
||||||
|
|
||||||
void PortsWindow::clearCurrentSelection()
|
void PortsWindow::clearCurrentSelection()
|
||||||
{
|
{
|
||||||
tvPortList->selectionModel()->clearCurrentIndex();
|
tvPortList->selectionModel()->clearCurrentIndex();
|
||||||
@ -321,30 +271,6 @@ void PortsWindow::showMyReservedPortsOnly(bool enabled)
|
|||||||
proxyPortModel->setFilterRegExp(QRegExp(""));
|
proxyPortModel->setFilterRegExp(QRegExp(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
{
|
|
||||||
qDebug("%s: invalid index", __FUNCTION__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug("stream list activated\n");
|
|
||||||
|
|
||||||
Port &curPort = plm->port(proxyPortModel ?
|
|
||||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
|
||||||
tvPortList->currentIndex());
|
|
||||||
|
|
||||||
QList<Stream*> streams;
|
|
||||||
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
|
||||||
|
|
||||||
StreamConfigDialog scd(streams, curPort, this);
|
|
||||||
if (scd.exec() == QDialog::Accepted) {
|
|
||||||
curPort.recalculateAverageRates();
|
|
||||||
curPort.setLocalConfigChanged(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||||
const QModelIndex& previousIndex)
|
const QModelIndex& previousIndex)
|
||||||
{
|
{
|
||||||
@ -356,16 +282,12 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
|||||||
previous = proxyPortModel->mapToSource(previous);
|
previous = proxyPortModel->mapToSource(previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
plm->getStreamModel()->setCurrentPortIndex(current);
|
|
||||||
updatePortViewActions(currentIndex);
|
updatePortViewActions(currentIndex);
|
||||||
updateStreamViewActions();
|
|
||||||
|
|
||||||
qDebug("In %s", __FUNCTION__);
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
if (previous.isValid() && plm->isPort(previous))
|
if (previous.isValid() && plm->isPort(previous))
|
||||||
{
|
{
|
||||||
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
|
|
||||||
this, SLOT(updatePortRates()));
|
|
||||||
disconnect(&(plm->port(previous)),
|
disconnect(&(plm->port(previous)),
|
||||||
SIGNAL(localConfigChanged(int, int, bool)),
|
SIGNAL(localConfigChanged(int, int, bool)),
|
||||||
this,
|
this,
|
||||||
@ -386,9 +308,6 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
|||||||
else if (plm->isPort(current))
|
else if (plm->isPort(current))
|
||||||
{
|
{
|
||||||
swDetail->setCurrentIndex(2); // port detail page
|
swDetail->setCurrentIndex(2); // port detail page
|
||||||
updatePortRates();
|
|
||||||
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
|
|
||||||
SLOT(updatePortRates()));
|
|
||||||
connect(&(plm->port(current)),
|
connect(&(plm->port(current)),
|
||||||
SIGNAL(localConfigChanged(int, int, bool)),
|
SIGNAL(localConfigChanged(int, int, bool)),
|
||||||
SLOT(updateApplyHint(int, int, bool)));
|
SLOT(updateApplyHint(int, int, bool)));
|
||||||
@ -446,139 +365,6 @@ void PortsWindow::when_portModel_reset()
|
|||||||
when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex());
|
when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortsWindow::on_startTx_clicked()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
QModelIndex curPortGroup = plm->getPortModel()->parent(current);
|
|
||||||
Q_ASSERT(curPortGroup.isValid());
|
|
||||||
Q_ASSERT(plm->isPortGroup(curPortGroup));
|
|
||||||
|
|
||||||
QList<uint> portList({plm->port(current).id()});
|
|
||||||
plm->portGroup(curPortGroup).startTx(&portList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_stopTx_clicked()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
QModelIndex curPortGroup = plm->getPortModel()->parent(current);
|
|
||||||
Q_ASSERT(curPortGroup.isValid());
|
|
||||||
Q_ASSERT(plm->isPortGroup(curPortGroup));
|
|
||||||
|
|
||||||
QList<uint> portList({plm->port(current).id()});
|
|
||||||
plm->portGroup(curPortGroup).stopTx(&portList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_averagePacketsPerSec_editingFinished()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
bool isOk;
|
|
||||||
double pps = XLocale().toDouble(averagePacketsPerSec->text(), &isOk);
|
|
||||||
|
|
||||||
plm->port(current).setAveragePacketRate(pps);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_averageBitsPerSec_editingFinished()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
bool isOk;
|
|
||||||
double bps = XLocale().toDouble(averageBitsPerSec->text(), &isOk);
|
|
||||||
|
|
||||||
plm->port(current).setAverageBitRate(bps);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::updatePortRates()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
if (!current.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!plm->isPort(current))
|
|
||||||
return;
|
|
||||||
|
|
||||||
averagePacketsPerSec->setText(QString("%L1")
|
|
||||||
.arg(plm->port(current).averagePacketRate(), 0, 'f', 4));
|
|
||||||
averageBitsPerSec->setText(QString("%L1")
|
|
||||||
.arg(plm->port(current).averageBitRate(), 0, 'f', 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::updateStreamViewActions()
|
|
||||||
{
|
|
||||||
QModelIndex current = tvPortList->currentIndex();
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
// For some reason hasSelection() returns true even if selection size is 0
|
|
||||||
// so additional check for size introduced
|
|
||||||
if (tvStreamList->selectionModel()->hasSelection() &&
|
|
||||||
(tvStreamList->selectionModel()->selection().size() > 0))
|
|
||||||
{
|
|
||||||
qDebug("Has selection %d",
|
|
||||||
tvStreamList->selectionModel()->selection().size());
|
|
||||||
|
|
||||||
// If more than one non-contiguous ranges selected,
|
|
||||||
// disable "New" and "Edit"
|
|
||||||
if (tvStreamList->selectionModel()->selection().size() > 1)
|
|
||||||
{
|
|
||||||
actionNew_Stream->setDisabled(true);
|
|
||||||
actionEdit_Stream->setDisabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
actionNew_Stream->setEnabled(true);
|
|
||||||
actionEdit_Stream->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicate/Delete are always enabled as long as we have a selection
|
|
||||||
actionDuplicate_Stream->setEnabled(true);
|
|
||||||
actionDelete_Stream->setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qDebug("No selection");
|
|
||||||
if (plm->isPort(current))
|
|
||||||
actionNew_Stream->setEnabled(true);
|
|
||||||
else
|
|
||||||
actionNew_Stream->setDisabled(true);
|
|
||||||
actionEdit_Stream->setDisabled(true);
|
|
||||||
actionDuplicate_Stream->setDisabled(true);
|
|
||||||
actionDelete_Stream->setDisabled(true);
|
|
||||||
}
|
|
||||||
actionOpen_Streams->setEnabled(plm->isPort(current));
|
|
||||||
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
|
||||||
|
|
||||||
startTx->setEnabled(tvStreamList->model()->rowCount() > 0);
|
|
||||||
stopTx->setEnabled(tvStreamList->model()->rowCount() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
|
void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
|
||||||
bool configChanged)
|
bool configChanged)
|
||||||
{
|
{
|
||||||
@ -586,7 +372,7 @@ void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
|
|||||||
applyHint->setText("Configuration has changed - "
|
applyHint->setText("Configuration has changed - "
|
||||||
"<font color='red'><b>click Apply</b></font> "
|
"<font color='red'><b>click Apply</b></font> "
|
||||||
"to activate the changes");
|
"to activate the changes");
|
||||||
else if (tvStreamList->model()->rowCount() > 0)
|
else if (plm->getStreamModel()->rowCount() > 0)
|
||||||
applyHint->setText("Configuration activated - "
|
applyHint->setText("Configuration activated - "
|
||||||
"click <img src=':/icons/control_play'/> "
|
"click <img src=':/icons/control_play'/> "
|
||||||
"to transmit packets");
|
"to transmit packets");
|
||||||
@ -838,258 +624,3 @@ void PortsWindow::on_actionPort_Configuration_triggered()
|
|||||||
if (dialog.exec() == QDialog::Accepted)
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
plm->portGroup(current.parent()).modifyPort(current.row(), config);
|
plm->portGroup(current.parent()).modifyPort(current.row(), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortsWindow::on_actionNew_Stream_triggered()
|
|
||||||
{
|
|
||||||
qDebug("New Stream Action");
|
|
||||||
|
|
||||||
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
|
||||||
if (selectionModel->selection().size() > 1) {
|
|
||||||
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
|
|
||||||
selectionModel->selection().size());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case nothing is selected, insert 1 row at the end
|
|
||||||
StreamModel *streamModel = plm->getStreamModel();
|
|
||||||
int row = streamModel->rowCount(), count = 1;
|
|
||||||
|
|
||||||
// In case we have a single range selected; insert as many rows as
|
|
||||||
// in the singe selected range before the top of the selected range
|
|
||||||
if (selectionModel->selection().size() == 1)
|
|
||||||
{
|
|
||||||
row = selectionModel->selection().at(0).top();
|
|
||||||
count = selectionModel->selection().at(0).height();
|
|
||||||
}
|
|
||||||
|
|
||||||
Port &curPort = plm->port(proxyPortModel ?
|
|
||||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
|
||||||
tvPortList->currentIndex());
|
|
||||||
|
|
||||||
QList<Stream*> streams;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
streams.append(new Stream);
|
|
||||||
|
|
||||||
StreamConfigDialog scd(streams, curPort, this);
|
|
||||||
scd.setWindowTitle(tr("Add Stream"));
|
|
||||||
if (scd.exec() == QDialog::Accepted)
|
|
||||||
streamModel->insert(row, streams);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_actionEdit_Stream_triggered()
|
|
||||||
{
|
|
||||||
qDebug("Edit Stream Action");
|
|
||||||
|
|
||||||
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
|
|
||||||
if (!streamModel->hasSelection())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Port &curPort = plm->port(proxyPortModel ?
|
|
||||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
|
||||||
tvPortList->currentIndex());
|
|
||||||
|
|
||||||
QList<Stream*> streams;
|
|
||||||
foreach(QModelIndex index, streamModel->selectedRows())
|
|
||||||
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
|
||||||
|
|
||||||
StreamConfigDialog scd(streams, curPort, this);
|
|
||||||
if (scd.exec() == QDialog::Accepted) {
|
|
||||||
curPort.recalculateAverageRates();
|
|
||||||
curPort.setLocalConfigChanged(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_actionDuplicate_Stream_triggered()
|
|
||||||
{
|
|
||||||
QItemSelectionModel* model = tvStreamList->selectionModel();
|
|
||||||
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
|
||||||
|
|
||||||
qDebug("Duplicate Stream Action");
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
if (model->hasSelection())
|
|
||||||
{
|
|
||||||
bool isOk;
|
|
||||||
int count = QInputDialog::getInt(this, "Duplicate Streams",
|
|
||||||
"Count", 1, 1, 9999, 1, &isOk);
|
|
||||||
|
|
||||||
if (!isOk)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QList<int> list;
|
|
||||||
foreach(QModelIndex index, model->selectedRows())
|
|
||||||
list.append(index.row());
|
|
||||||
plm->port(current).duplicateStreams(list, count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
qDebug("No selection");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_actionDelete_Stream_triggered()
|
|
||||||
{
|
|
||||||
qDebug("Delete Stream Action");
|
|
||||||
|
|
||||||
QModelIndex index;
|
|
||||||
|
|
||||||
if (tvStreamList->selectionModel()->hasSelection())
|
|
||||||
{
|
|
||||||
qDebug("SelectedIndexes %d",
|
|
||||||
tvStreamList->selectionModel()->selectedRows().size());
|
|
||||||
while(tvStreamList->selectionModel()->selectedRows().size())
|
|
||||||
{
|
|
||||||
index = tvStreamList->selectionModel()->selectedRows().at(0);
|
|
||||||
plm->getStreamModel()->removeRows(index.row(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
qDebug("No selection");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_actionOpen_Streams_triggered()
|
|
||||||
{
|
|
||||||
qDebug("Open Streams Action");
|
|
||||||
|
|
||||||
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
|
|
||||||
StreamFileFormat::kOpenFile);
|
|
||||||
QString fileType;
|
|
||||||
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
|
||||||
static QString dirName;
|
|
||||||
QString fileName;
|
|
||||||
QString errorStr;
|
|
||||||
bool append = true;
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
if (fileTypes.size())
|
|
||||||
fileType = fileTypes.at(0);
|
|
||||||
|
|
||||||
fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"),
|
|
||||||
dirName, fileTypes.join(";;"), &fileType);
|
|
||||||
if (fileName.isEmpty())
|
|
||||||
goto _exit;
|
|
||||||
|
|
||||||
if (tvStreamList->model()->rowCount())
|
|
||||||
{
|
|
||||||
QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(),
|
|
||||||
tr("Append to existing streams? Or overwrite?"),
|
|
||||||
QMessageBox::NoButton, this);
|
|
||||||
QPushButton *appendBtn = msgBox.addButton(tr("Append"),
|
|
||||||
QMessageBox::ActionRole);
|
|
||||||
QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"),
|
|
||||||
QMessageBox::ActionRole);
|
|
||||||
QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel);
|
|
||||||
|
|
||||||
msgBox.exec();
|
|
||||||
|
|
||||||
if (msgBox.clickedButton() == cancelBtn)
|
|
||||||
goto _exit;
|
|
||||||
else if (msgBox.clickedButton() == appendBtn)
|
|
||||||
append = true;
|
|
||||||
else if (msgBox.clickedButton() == overwriteBtn)
|
|
||||||
append = false;
|
|
||||||
else
|
|
||||||
Q_ASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = plm->port(current).openStreams(fileName, append, errorStr);
|
|
||||||
if (!ret || !errorStr.isEmpty())
|
|
||||||
{
|
|
||||||
QMessageBox msgBox(this);
|
|
||||||
QStringList str = errorStr.split("\n\n\n\n");
|
|
||||||
|
|
||||||
msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical);
|
|
||||||
msgBox.setWindowTitle(qApp->applicationName());
|
|
||||||
msgBox.setText(str.at(0));
|
|
||||||
if (str.size() > 1)
|
|
||||||
msgBox.setDetailedText(str.at(1));
|
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
|
||||||
|
|
||||||
msgBox.exec();
|
|
||||||
}
|
|
||||||
dirName = QFileInfo(fileName).absolutePath();
|
|
||||||
|
|
||||||
_exit:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortsWindow::on_actionSave_Streams_triggered()
|
|
||||||
{
|
|
||||||
qDebug("Save Streams Action");
|
|
||||||
|
|
||||||
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
|
||||||
static QString fileName;
|
|
||||||
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
|
|
||||||
StreamFileFormat::kSaveFile);
|
|
||||||
QString fileType;
|
|
||||||
QString errorStr;
|
|
||||||
QFileDialog::Options options;
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
current = proxyPortModel->mapToSource(current);
|
|
||||||
|
|
||||||
// On Mac OS with Native Dialog, getSaveFileName() ignores fileType
|
|
||||||
// which we need
|
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
options |= QFileDialog::DontUseNativeDialog;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (fileTypes.size())
|
|
||||||
fileType = fileTypes.at(0);
|
|
||||||
|
|
||||||
Q_ASSERT(plm->isPort(current));
|
|
||||||
|
|
||||||
_retry:
|
|
||||||
fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"),
|
|
||||||
fileName, fileTypes.join(";;"), &fileType, options);
|
|
||||||
if (fileName.isEmpty())
|
|
||||||
goto _exit;
|
|
||||||
|
|
||||||
if (QFileInfo(fileName).suffix().isEmpty()) {
|
|
||||||
QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1);
|
|
||||||
qDebug("Adding extension '%s' to '%s'",
|
|
||||||
qPrintable(fileExt), qPrintable(fileName));
|
|
||||||
fileName.append(fileExt);
|
|
||||||
if (QFileInfo(fileName).exists()) {
|
|
||||||
if (QMessageBox::warning(this, tr("Overwrite File?"),
|
|
||||||
QString("The file \"%1\" already exists.\n\n"
|
|
||||||
"Do you wish to overwrite it?")
|
|
||||||
.arg(QFileInfo(fileName).fileName()),
|
|
||||||
QMessageBox::Yes|QMessageBox::No,
|
|
||||||
QMessageBox::No) != QMessageBox::Yes)
|
|
||||||
goto _retry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed();
|
|
||||||
if (!fileType.startsWith("Ostinato")
|
|
||||||
&& !fileType.startsWith("Python"))
|
|
||||||
{
|
|
||||||
if (QMessageBox::warning(this, tr("Ostinato"),
|
|
||||||
QString("You have chosen to save in %1 format. All stream "
|
|
||||||
"attributes may not be saved in this format.\n\n"
|
|
||||||
"It is recommended to save in native Ostinato format.\n\n"
|
|
||||||
"Continue to save in %2 format?").arg(fileType).arg(fileType),
|
|
||||||
QMessageBox::Yes|QMessageBox::No,
|
|
||||||
QMessageBox::No) != QMessageBox::Yes)
|
|
||||||
goto _retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: all or selected?
|
|
||||||
|
|
||||||
if (!plm->port(current).saveStreams(fileName, fileType, errorStr))
|
|
||||||
QMessageBox::critical(this, qApp->applicationName(), errorStr);
|
|
||||||
else if (!errorStr.isEmpty())
|
|
||||||
QMessageBox::warning(this, qApp->applicationName(), errorStr);
|
|
||||||
|
|
||||||
fileName = QFileInfo(fileName).absolutePath();
|
|
||||||
_exit:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,13 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#ifndef _PORTS_WINDOW_H
|
#ifndef _PORTS_WINDOW_H
|
||||||
#define _PORTS_WINDOW_H
|
#define _PORTS_WINDOW_H
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include "ui_portswindow.h"
|
#include "ui_portswindow.h"
|
||||||
#include "portgrouplist.h"
|
#include <QWidget>
|
||||||
|
|
||||||
class ApplyMessage;
|
class ApplyMessage;
|
||||||
class QAbstractItemDelegate;
|
class PortGroupList;
|
||||||
|
|
||||||
class QProgressDialog;
|
class QProgressDialog;
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
|
|
||||||
@ -38,9 +37,6 @@ class PortsWindow : public QWidget, private Ui::PortsWindow
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
//QAbstractItemModel *slm; // stream list model
|
|
||||||
PortGroupList *plm;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PortsWindow(PortGroupList *pgl, QWidget *parent = 0);
|
PortsWindow(PortGroupList *pgl, QWidget *parent = 0);
|
||||||
~PortsWindow();
|
~PortsWindow();
|
||||||
@ -54,16 +50,14 @@ public:
|
|||||||
QString &error,
|
QString &error,
|
||||||
QProgressDialog *progress = NULL);
|
QProgressDialog *progress = NULL);
|
||||||
|
|
||||||
|
QList<QAction*> portActions();
|
||||||
|
QList<QAction*> streamActions();
|
||||||
|
QList<QAction*> deviceActions();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void currentPortChanged(const QModelIndex ¤t,
|
void currentPortChanged(const QModelIndex ¤t,
|
||||||
const QModelIndex &previous);
|
const QModelIndex &previous);
|
||||||
|
|
||||||
private:
|
|
||||||
QString lastNewPortGroup;
|
|
||||||
QAbstractItemDelegate *delegate;
|
|
||||||
QSortFilterProxyModel *proxyPortModel;
|
|
||||||
ApplyMessage *applyMsg_;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void clearCurrentSelection();
|
void clearCurrentSelection();
|
||||||
void showMyReservedPortsOnly(bool enabled);
|
void showMyReservedPortsOnly(bool enabled);
|
||||||
@ -71,14 +65,7 @@ public slots:
|
|||||||
private slots:
|
private slots:
|
||||||
void updateApplyHint(int portGroupId, int portId, bool configChanged);
|
void updateApplyHint(int portGroupId, int portId, bool configChanged);
|
||||||
void updatePortViewActions(const QModelIndex& currentIndex);
|
void updatePortViewActions(const QModelIndex& currentIndex);
|
||||||
void updateStreamViewActions();
|
|
||||||
|
|
||||||
void on_startTx_clicked();
|
|
||||||
void on_stopTx_clicked();
|
|
||||||
void on_averagePacketsPerSec_editingFinished();
|
|
||||||
void on_averageBitsPerSec_editingFinished();
|
|
||||||
void updatePortRates();
|
|
||||||
void on_tvStreamList_activated(const QModelIndex & index);
|
|
||||||
void when_portView_currentChanged(const QModelIndex& currentIndex,
|
void when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||||
const QModelIndex& previousIndex);
|
const QModelIndex& previousIndex);
|
||||||
void when_portModel_dataChanged(const QModelIndex& topLeft,
|
void when_portModel_dataChanged(const QModelIndex& topLeft,
|
||||||
@ -95,15 +82,11 @@ private slots:
|
|||||||
void on_actionExclusive_Control_triggered(bool checked);
|
void on_actionExclusive_Control_triggered(bool checked);
|
||||||
void on_actionPort_Configuration_triggered();
|
void on_actionPort_Configuration_triggered();
|
||||||
|
|
||||||
void on_actionNew_Stream_triggered();
|
private:
|
||||||
void on_actionEdit_Stream_triggered();
|
PortGroupList *plm;
|
||||||
void on_actionDuplicate_Stream_triggered();
|
QString lastNewPortGroup;
|
||||||
void on_actionDelete_Stream_triggered();
|
QSortFilterProxyModel *proxyPortModel;
|
||||||
|
ApplyMessage *applyMsg_;
|
||||||
void on_actionOpen_Streams_triggered();
|
|
||||||
void on_actionSave_Streams_triggered();
|
|
||||||
|
|
||||||
void streamModelDataChanged();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
<widget class="QStackedWidget" name="swDetail">
|
<widget class="QStackedWidget" name="swDetail">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
<horstretch>2</horstretch>
|
<horstretch>1</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
@ -126,7 +126,16 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="portDetail">
|
<widget class="QWidget" name="portDetail">
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -138,7 +147,16 @@
|
|||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -195,114 +213,10 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<widget class="PortWidget" name="portWidget" native="true"/>
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="startTx">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Start Transmit</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string>Start transmit on selected port</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/control_play.png</normaloff>:/icons/control_play.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="stopTx">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Stop Transmit</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string>Stop transmit on selected port</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/control_stop.png</normaloff>:/icons/control_stop.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Avg pps</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="averagePacketsPerSec"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioButton_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Avg bps</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="averageBitsPerSec">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="XTableView" name="tvStreamList">
|
<widget class="StreamsWidget" name="streamsWidget" native="true"/>
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::ActionsContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="whatsThis">
|
|
||||||
<string>This is the stream list for the selected port
|
|
||||||
|
|
||||||
A stream is a sequence of one or more packets
|
|
||||||
|
|
||||||
Right-click to create a stream</string>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="lineWidth">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="selectionMode">
|
|
||||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -360,33 +274,6 @@ Right-click to create a stream</string>
|
|||||||
<string>Disconnect Port Group</string>
|
<string>Disconnect Port Group</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionNew_Stream">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>New Stream</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionDelete_Stream">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Delete Stream</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionEdit_Stream">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Edit Stream</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionExclusive_Control">
|
<action name="actionExclusive_Control">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -395,30 +282,11 @@ Right-click to create a stream</string>
|
|||||||
<string>Exclusive Port Control (EXPERIMENTAL)</string>
|
<string>Exclusive Port Control (EXPERIMENTAL)</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionOpen_Streams">
|
|
||||||
<property name="text">
|
|
||||||
<string>Open Streams ...</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionSave_Streams">
|
|
||||||
<property name="text">
|
|
||||||
<string>Save Streams ...</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionPort_Configuration">
|
<action name="actionPort_Configuration">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Port Configuration ...</string>
|
<string>Port Configuration ...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDuplicate_Stream">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="ostinato.qrc">
|
|
||||||
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Duplicate Stream</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
@ -433,46 +301,20 @@ Right-click to create a stream</string>
|
|||||||
<header>xtreeview.h</header>
|
<header>xtreeview.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>XTableView</class>
|
<class>StreamsWidget</class>
|
||||||
<extends>QTableView</extends>
|
<extends>QWidget</extends>
|
||||||
<header>xtableview.h</header>
|
<header>streamswidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>PortWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>portwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="ostinato.qrc"/>
|
<include location="ostinato.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections/>
|
||||||
<connection>
|
|
||||||
<sender>radioButton</sender>
|
|
||||||
<signal>toggled(bool)</signal>
|
|
||||||
<receiver>averagePacketsPerSec</receiver>
|
|
||||||
<slot>setEnabled(bool)</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>326</x>
|
|
||||||
<y>80</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>454</x>
|
|
||||||
<y>79</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>radioButton_2</sender>
|
|
||||||
<signal>toggled(bool)</signal>
|
|
||||||
<receiver>averageBitsPerSec</receiver>
|
|
||||||
<slot>setEnabled(bool)</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>523</x>
|
|
||||||
<y>80</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>651</x>
|
|
||||||
<y>88</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
</ui>
|
||||||
|
182
client/portwidget.cpp
Normal file
182
client/portwidget.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 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 "portwidget.h"
|
||||||
|
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "xqlocale.h"
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
|
PortWidget::PortWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::setPortGroupList(PortGroupList *portGroups)
|
||||||
|
{
|
||||||
|
plm = portGroups;
|
||||||
|
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
|
||||||
|
SLOT(updatePortActions()));
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
|
||||||
|
SLOT(updatePortActions()));
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(modelReset()),
|
||||||
|
SLOT(updatePortActions()));
|
||||||
|
|
||||||
|
updatePortActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
PortWidget::~PortWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::setCurrentPortIndex(const QModelIndex &portIndex)
|
||||||
|
{
|
||||||
|
if (!plm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel
|
||||||
|
if (!plm->isPort(portIndex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
// Disconnect previous port
|
||||||
|
if (plm->isPort(currentPortIndex_))
|
||||||
|
disconnect(&(plm->port(currentPortIndex_)),
|
||||||
|
SIGNAL(portRateChanged(int, int)),
|
||||||
|
this, SLOT(updatePortRates()));
|
||||||
|
|
||||||
|
currentPortIndex_ = portIndex;
|
||||||
|
|
||||||
|
// Connect current port
|
||||||
|
if (plm->isPort(currentPortIndex_))
|
||||||
|
connect(&(plm->port(currentPortIndex_)),
|
||||||
|
SIGNAL(portRateChanged(int, int)),
|
||||||
|
this, SLOT(updatePortRates()));
|
||||||
|
|
||||||
|
double speed = plm->port(currentPortIndex_).speed();
|
||||||
|
portSpeed->setText(QString("Max %L1 Mbps").arg(speed));
|
||||||
|
|
||||||
|
rbLoad->setVisible(speed > 0);
|
||||||
|
averageLoadPercent->setVisible(speed > 0);
|
||||||
|
speedSep->setVisible(speed > 0);
|
||||||
|
portSpeed->setVisible(speed > 0);
|
||||||
|
|
||||||
|
updatePortRates();
|
||||||
|
updatePortActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::on_startTx_clicked()
|
||||||
|
{
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
QModelIndex curPortGroup = plm->getPortModel()->parent(currentPortIndex_);
|
||||||
|
Q_ASSERT(curPortGroup.isValid());
|
||||||
|
Q_ASSERT(plm->isPortGroup(curPortGroup));
|
||||||
|
|
||||||
|
QList<uint> portList({plm->port(currentPortIndex_).id()});
|
||||||
|
plm->portGroup(curPortGroup).startTx(&portList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::on_stopTx_clicked()
|
||||||
|
{
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
QModelIndex curPortGroup = plm->getPortModel()->parent(currentPortIndex_);
|
||||||
|
Q_ASSERT(curPortGroup.isValid());
|
||||||
|
Q_ASSERT(plm->isPortGroup(curPortGroup));
|
||||||
|
|
||||||
|
QList<uint> portList({plm->port(currentPortIndex_).id()});
|
||||||
|
plm->portGroup(curPortGroup).stopTx(&portList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::on_averageLoadPercent_editingFinished()
|
||||||
|
{
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
plm->port(currentPortIndex_).setAverageLoadRate(
|
||||||
|
averageLoadPercent->value()/100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::on_averagePacketsPerSec_editingFinished()
|
||||||
|
{
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
bool isOk;
|
||||||
|
double pps = XLocale().toPacketsPerSecond(averagePacketsPerSec->text(),
|
||||||
|
&isOk);
|
||||||
|
|
||||||
|
if (isOk)
|
||||||
|
plm->port(currentPortIndex_).setAveragePacketRate(pps);
|
||||||
|
else
|
||||||
|
updatePortRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::on_averageBitsPerSec_editingFinished()
|
||||||
|
{
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
bool isOk;
|
||||||
|
double bps = XLocale().toBitsPerSecond(averageBitsPerSec->text(), &isOk);
|
||||||
|
|
||||||
|
if (isOk)
|
||||||
|
plm->port(currentPortIndex_).setAverageBitRate(bps);
|
||||||
|
else
|
||||||
|
updatePortRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::updatePortRates()
|
||||||
|
{
|
||||||
|
if (!currentPortIndex_.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!plm->isPort(currentPortIndex_))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// XXX: pps/bps input widget is a LineEdit and not a SpinBox
|
||||||
|
// because we want users to be able to enter values in various
|
||||||
|
// units e.g. "1.5 Mbps", "1000K", "50" (bps) etc.
|
||||||
|
|
||||||
|
// XXX: It's a considered decision NOT to show frame rate in
|
||||||
|
// higher units of Kpps and Mpps as most users may not be
|
||||||
|
// familiar with those and also we want frame rate to have a
|
||||||
|
// high resolution for input e.g. if user enters 1,488,095.2381
|
||||||
|
// it should NOT be shown as 1.4881 Mpps
|
||||||
|
|
||||||
|
averagePacketsPerSec->setText(QString("%L1 pps")
|
||||||
|
.arg(plm->port(currentPortIndex_).averagePacketRate(), 0, 'f', 4));
|
||||||
|
|
||||||
|
averageBitsPerSec->setText(XLocale().toBitRateString(
|
||||||
|
plm->port(currentPortIndex_).averageBitRate()));
|
||||||
|
|
||||||
|
averageLoadPercent->setValue(
|
||||||
|
plm->port(currentPortIndex_).averageLoadRate()*100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortWidget::updatePortActions()
|
||||||
|
{
|
||||||
|
if (!plm->isPort(currentPortIndex_))
|
||||||
|
return;
|
||||||
|
|
||||||
|
startTx->setEnabled(plm->port(currentPortIndex_).numStreams() > 0);
|
||||||
|
stopTx->setEnabled(plm->port(currentPortIndex_).numStreams() > 0);
|
||||||
|
}
|
60
client/portwidget.h
Normal file
60
client/portwidget.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 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 _PORT_WIDGET_H
|
||||||
|
#define _PORT_WIDGET_H
|
||||||
|
|
||||||
|
#include "ui_portwidget.h"
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class PortGroupList;
|
||||||
|
|
||||||
|
class PortWidget : public QWidget, private Ui::PortWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortWidget(QWidget *parent = 0);
|
||||||
|
~PortWidget();
|
||||||
|
|
||||||
|
void setPortGroupList(PortGroupList *portGroups);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setCurrentPortIndex(const QModelIndex &portIndex);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
|
||||||
|
void on_startTx_clicked();
|
||||||
|
void on_stopTx_clicked();
|
||||||
|
void on_averageLoadPercent_editingFinished();
|
||||||
|
void on_averagePacketsPerSec_editingFinished();
|
||||||
|
void on_averageBitsPerSec_editingFinished();
|
||||||
|
|
||||||
|
void updatePortActions();
|
||||||
|
void updatePortRates();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortGroupList *plm{nullptr}; // FIXME: rename to portGroups_?
|
||||||
|
QModelIndex currentPortIndex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
218
client/portwidget.ui
Normal file
218
client/portwidget.ui
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PortWidget</class>
|
||||||
|
<widget class="QWidget" name="PortWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>806</width>
|
||||||
|
<height>73</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="startTx">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Start Transmit</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip">
|
||||||
|
<string>Start transmit on selected port</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/control_play.png</normaloff>:/icons/control_play.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="stopTx">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Stop Transmit</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip">
|
||||||
|
<string>Stop transmit on selected port</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/control_stop.png</normaloff>:/icons/control_stop.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="rateSep">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Frame Rate</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="averagePacketsPerSec"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bit Rate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="averageBitsPerSec">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Bit rate on the line including overhead such as Preamble, IPG, FCS etc.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="rbLoad">
|
||||||
|
<property name="text">
|
||||||
|
<string>Load</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDoubleSpinBox" name="averageLoadPercent">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string>%</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>999.999900000000025</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="speedSep">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="portSpeed">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Port Speed</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Max speed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>radioButton</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>averagePacketsPerSec</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>450</x>
|
||||||
|
<y>44</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>593</x>
|
||||||
|
<y>45</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>radioButton_2</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>averageBitsPerSec</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>661</x>
|
||||||
|
<y>44</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>804</x>
|
||||||
|
<y>45</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>rbLoad</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>averageLoadPercent</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>281</x>
|
||||||
|
<y>43</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>308</x>
|
||||||
|
<y>45</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "../common/ostprotolib.h"
|
#include "../common/ostprotolib.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "thememanager.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
@ -40,6 +41,7 @@ Preferences::Preferences()
|
|||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
|
// Program paths
|
||||||
wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey,
|
wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey,
|
||||||
kWiresharkPathDefaultValue).toString());
|
kWiresharkPathDefaultValue).toString());
|
||||||
tsharkPathEdit->setText(appSettings->value(kTsharkPathKey,
|
tsharkPathEdit->setText(appSettings->value(kTsharkPathKey,
|
||||||
@ -51,6 +53,10 @@ Preferences::Preferences()
|
|||||||
awkPathEdit->setText(appSettings->value(kAwkPathKey,
|
awkPathEdit->setText(appSettings->value(kAwkPathKey,
|
||||||
kAwkPathDefaultValue).toString());
|
kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
// Theme
|
||||||
|
theme->addItems(ThemeManager::instance()->themeList());
|
||||||
|
theme->setCurrentText(appSettings->value(kThemeKey).toString());
|
||||||
|
|
||||||
// TODO(only if required): kUserKey
|
// TODO(only if required): kUserKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +84,7 @@ void Preferences::initDefaults()
|
|||||||
|
|
||||||
void Preferences::accept()
|
void Preferences::accept()
|
||||||
{
|
{
|
||||||
|
// Program paths
|
||||||
appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text());
|
appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text());
|
||||||
appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text());
|
appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text());
|
||||||
appSettings->setValue(kGzipPathKey, gzipPathEdit->text());
|
appSettings->setValue(kGzipPathKey, gzipPathEdit->text());
|
||||||
@ -90,6 +97,9 @@ void Preferences::accept()
|
|||||||
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||||
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
// Theme
|
||||||
|
ThemeManager::instance()->setTheme(theme->currentText());
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<ui version="4.0" >
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
<class>Preferences</class>
|
<class>Preferences</class>
|
||||||
<widget class="QDialog" name="Preferences" >
|
<widget class="QDialog" name="Preferences">
|
||||||
<property name="geometry" >
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
@ -9,148 +10,149 @@
|
|||||||
<height>220</height>
|
<height>220</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle">
|
||||||
<string>Preferences</string>
|
<string>Preferences</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon" >
|
<property name="windowIcon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/preferences.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/preferences.png</normaloff>:/icons/preferences.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame" >
|
<widget class="QFrame" name="frame">
|
||||||
<property name="frameShape" >
|
<property name="frameShape">
|
||||||
<enum>QFrame::Box</enum>
|
<enum>QFrame::Box</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShadow" >
|
<property name="frameShadow">
|
||||||
<enum>QFrame::Sunken</enum>
|
<enum>QFrame::Sunken</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" >
|
<layout class="QGridLayout">
|
||||||
<item row="0" column="0" >
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label" >
|
<widget class="QLabel" name="label">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>'wireshark' Path</string>
|
<string>'wireshark' Path</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>wiresharkPathEdit</cstring>
|
<cstring>wiresharkPathEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1" >
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="wiresharkPathEdit" >
|
<widget class="QLineEdit" name="wiresharkPathEdit">
|
||||||
<property name="enabled" >
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2" >
|
<item row="0" column="2">
|
||||||
<widget class="QToolButton" name="wiresharkPathButton" >
|
<widget class="QToolButton" name="wiresharkPathButton">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" >
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_2" >
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>'tshark' Path</string>
|
<string>'tshark' Path</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>tsharkPathEdit</cstring>
|
<cstring>tsharkPathEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1" >
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="tsharkPathEdit" >
|
<widget class="QLineEdit" name="tsharkPathEdit">
|
||||||
<property name="enabled" >
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2" >
|
<item row="1" column="2">
|
||||||
<widget class="QToolButton" name="tsharkPathButton" >
|
<widget class="QToolButton" name="tsharkPathButton">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" >
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_5" >
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>'gzip' Path</string>
|
<string>'gzip' Path</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>diffPathEdit</cstring>
|
<cstring>diffPathEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1" >
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="gzipPathEdit" >
|
<widget class="QLineEdit" name="gzipPathEdit">
|
||||||
<property name="enabled" >
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2" >
|
<item row="2" column="2">
|
||||||
<widget class="QToolButton" name="gzipPathButton" >
|
<widget class="QToolButton" name="gzipPathButton">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" >
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_3" >
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>'diff' Path</string>
|
<string>'diff' Path</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>diffPathEdit</cstring>
|
<cstring>diffPathEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1" >
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="diffPathEdit" >
|
<widget class="QLineEdit" name="diffPathEdit">
|
||||||
<property name="enabled" >
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="2" >
|
<item row="3" column="2">
|
||||||
<widget class="QToolButton" name="diffPathButton" >
|
<widget class="QToolButton" name="diffPathButton">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" >
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_4" >
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>'awk' Path</string>
|
<string>'awk' Path</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>awkPathEdit</cstring>
|
<cstring>awkPathEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1" >
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="awkPathEdit" >
|
<widget class="QLineEdit" name="awkPathEdit">
|
||||||
<property name="enabled" >
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2" >
|
<item row="4" column="2">
|
||||||
<widget class="QToolButton" name="awkPathButton" >
|
<widget class="QToolButton" name="awkPathButton">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1" >
|
<item row="5" column="1">
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation" >
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>21</width>
|
<width>21</width>
|
||||||
<height>61</height>
|
<height>61</height>
|
||||||
@ -162,12 +164,26 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="orientation" >
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Theme (Experimental)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="theme"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons" >
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -187,7 +203,7 @@
|
|||||||
<tabstop>buttonBox</tabstop>
|
<tabstop>buttonBox</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="ostinato.qrc" />
|
<include location="ostinato.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
@ -196,11 +212,11 @@
|
|||||||
<receiver>Preferences</receiver>
|
<receiver>Preferences</receiver>
|
||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>248</x>
|
||||||
<y>254</y>
|
<y>254</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
<y>274</y>
|
<y>274</y>
|
||||||
</hint>
|
</hint>
|
||||||
@ -212,11 +228,11 @@
|
|||||||
<receiver>Preferences</receiver>
|
<receiver>Preferences</receiver>
|
||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>316</x>
|
||||||
<y>260</y>
|
<y>260</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
<y>274</y>
|
<y>274</y>
|
||||||
</hint>
|
</hint>
|
||||||
|
@ -74,6 +74,8 @@ const QString kAwkPathDefaultValue("/usr/bin/awk");
|
|||||||
const QString kAwkPathDefaultValue("/usr/bin/awk");
|
const QString kAwkPathDefaultValue("/usr/bin/awk");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const QString kThemeKey("Theme");
|
||||||
|
|
||||||
const QString kUserKey("User");
|
const QString kUserKey("User");
|
||||||
extern QString kUserDefaultValue;
|
extern QString kUserDefaultValue;
|
||||||
|
|
||||||
|
@ -432,6 +432,12 @@ void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode)
|
|||||||
lePktLenMin->setEnabled(true);
|
lePktLenMin->setEnabled(true);
|
||||||
lePktLenMax->setEnabled(true);
|
lePktLenMax->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
else if (mode == "IMIX")
|
||||||
|
{
|
||||||
|
lePktLen->setDisabled(true);
|
||||||
|
lePktLenMin->setDisabled(true);
|
||||||
|
lePktLenMax->setDisabled(true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qWarning("Unhandled/Unknown PktLenMode = %s", qPrintable(mode));
|
qWarning("Unhandled/Unknown PktLenMode = %s", qPrintable(mode));
|
||||||
@ -1165,12 +1171,7 @@ void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text)
|
|||||||
{
|
{
|
||||||
bool isOk;
|
bool isOk;
|
||||||
Stream *pStream = mpStream;
|
Stream *pStream = mpStream;
|
||||||
uint frameLen;
|
uint frameLen = pStream->frameLenAvg();
|
||||||
|
|
||||||
if (pStream->lenMode() == Stream::e_fl_fixed)
|
|
||||||
frameLen = pStream->frameLen();
|
|
||||||
else
|
|
||||||
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
|
|
||||||
|
|
||||||
if (rbSendPackets->isChecked())
|
if (rbSendPackets->isChecked())
|
||||||
{
|
{
|
||||||
@ -1189,13 +1190,9 @@ void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text)
|
|||||||
bool isOk;
|
bool isOk;
|
||||||
Stream *pStream = mpStream;
|
Stream *pStream = mpStream;
|
||||||
uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
|
uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
|
||||||
uint frameLen;
|
uint frameLen = pStream->frameLenAvg();
|
||||||
|
|
||||||
qDebug("start of %s(%s)", __FUNCTION__, qPrintable(text));
|
qDebug("start of %s(%s)", __FUNCTION__, qPrintable(text));
|
||||||
if (pStream->lenMode() == Stream::e_fl_fixed)
|
|
||||||
frameLen = pStream->frameLen();
|
|
||||||
else
|
|
||||||
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
|
|
||||||
|
|
||||||
if (rbSendBursts->isChecked())
|
if (rbSendBursts->isChecked())
|
||||||
{
|
{
|
||||||
@ -1215,12 +1212,7 @@ void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text)
|
|||||||
bool isOk;
|
bool isOk;
|
||||||
Stream *pStream = mpStream;
|
Stream *pStream = mpStream;
|
||||||
uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
|
uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
|
||||||
uint frameLen;
|
uint frameLen = pStream->frameLenAvg();
|
||||||
|
|
||||||
if (pStream->lenMode() == Stream::e_fl_fixed)
|
|
||||||
frameLen = pStream->frameLen();
|
|
||||||
else
|
|
||||||
frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2;
|
|
||||||
|
|
||||||
if (rbSendPackets->isChecked())
|
if (rbSendPackets->isChecked())
|
||||||
{
|
{
|
||||||
|
@ -108,6 +108,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-colo
|
|||||||
<string>Random</string>
|
<string>Random</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>IMIX</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
@ -670,6 +675,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-colo
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_2">
|
<widget class="QWidget" name="page_2">
|
||||||
|
@ -20,8 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "streamstatsmodel.h"
|
#include "streamstatsmodel.h"
|
||||||
|
|
||||||
#include "protocol.pb.h"
|
#include "protocol.pb.h"
|
||||||
|
#include "xqlocale.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QPalette>
|
||||||
|
|
||||||
// XXX: Keep the enum in sync with it's string
|
// XXX: Keep the enum in sync with it's string
|
||||||
enum {
|
enum {
|
||||||
@ -42,12 +46,22 @@ enum {
|
|||||||
kAggrTxPkts,
|
kAggrTxPkts,
|
||||||
kAggrRxPkts,
|
kAggrRxPkts,
|
||||||
kAggrPktLoss,
|
kAggrPktLoss,
|
||||||
|
kTxDuration,
|
||||||
|
kAvgTxFrameRate,
|
||||||
|
kAvgRxFrameRate,
|
||||||
|
kAvgTxBitRate,
|
||||||
|
kAvgRxBitRate,
|
||||||
kMaxAggrStreamStats
|
kMaxAggrStreamStats
|
||||||
};
|
};
|
||||||
static QStringList aggrStatTitles = QStringList()
|
static QStringList aggrStatTitles = QStringList()
|
||||||
<< "Total\nTx Pkts"
|
<< "Total\nTx Pkts"
|
||||||
<< "Total\nRx Pkts"
|
<< "Total\nRx Pkts"
|
||||||
<< "Total\nPkt Loss";
|
<< "Total\nPkt Loss"
|
||||||
|
<< "Duration\n(secs)"
|
||||||
|
<< "Avg\nTx PktRate"
|
||||||
|
<< "Avg\nRx PktRate"
|
||||||
|
<< "Avg\nTx BitRate"
|
||||||
|
<< "Avg\nRx BitRate";
|
||||||
|
|
||||||
static const uint kAggrGuid = 0xffffffff;
|
static const uint kAggrGuid = 0xffffffff;
|
||||||
|
|
||||||
@ -103,20 +117,35 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
|||||||
return Qt::AlignRight;
|
return Qt::AlignRight;
|
||||||
|
|
||||||
int portColumn = index.column() - kMaxAggrStreamStats;
|
int portColumn = index.column() - kMaxAggrStreamStats;
|
||||||
if (role == Qt::BackgroundRole) {
|
|
||||||
if (portColumn < 0)
|
// Stylesheets typically don't use or set palette colors, so if
|
||||||
return QBrush(QColor("lavender")); // Aggregate Column
|
// using one, don't use palette colors
|
||||||
if (index.row() == (guidList_.size() - 1))
|
if ((role == Qt::BackgroundRole) && qApp->styleSheet().isEmpty()) {
|
||||||
return QBrush(QColor("burlywood")); // Aggregate Row
|
QPalette palette = QApplication::palette();
|
||||||
else if ((portColumn/kMaxStreamStats) & 1)
|
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
||||||
return QBrush(QColor("beige")); // Color alternate Ports
|
return palette.dark();
|
||||||
|
if (portColumn < 0) // Aggregate Column
|
||||||
|
return palette.alternateBase();
|
||||||
|
if ((portColumn/kMaxStreamStats) & 1) // Color alternate Ports
|
||||||
|
return palette.alternateBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
Guid guid = guidList_.at(index.row());
|
Guid guid = guidList_.at(index.row());
|
||||||
if (role == Qt::ForegroundRole) {
|
if ((role == Qt::ForegroundRole && qApp->styleSheet().isEmpty())) {
|
||||||
|
QPalette palette = QApplication::palette();
|
||||||
if ((index.column() == kAggrPktLoss)
|
if ((index.column() == kAggrPktLoss)
|
||||||
&& aggrGuidStats_.value(guid).pktLoss)
|
&& aggrGuidStats_.value(guid).pktLoss)
|
||||||
return QBrush(QColor("firebrick"));
|
return palette.link();
|
||||||
|
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
||||||
|
return palette.brightText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == Qt::FontRole ) {
|
||||||
|
if (index.row() == (guidList_.size() - 1)) { // Aggregate Row
|
||||||
|
QFont font;
|
||||||
|
font.setBold(true);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role != Qt::DisplayRole)
|
if (role != Qt::DisplayRole)
|
||||||
@ -131,6 +160,30 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
|||||||
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
|
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
|
||||||
case kAggrPktLoss:
|
case kAggrPktLoss:
|
||||||
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
|
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
|
||||||
|
case kTxDuration:
|
||||||
|
return QString("%L1").arg(aggrGuidStats_.value(guid).txDuration);
|
||||||
|
case kAvgTxFrameRate:
|
||||||
|
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||||
|
QString("%L1").arg(
|
||||||
|
aggrGuidStats_.value(guid).txPkts
|
||||||
|
/ aggrGuidStats_.value(guid).txDuration);
|
||||||
|
case kAvgRxFrameRate:
|
||||||
|
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||||
|
QString("%L1").arg(
|
||||||
|
aggrGuidStats_.value(guid).rxPkts
|
||||||
|
/ aggrGuidStats_.value(guid).txDuration);
|
||||||
|
case kAvgTxBitRate:
|
||||||
|
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||||
|
XLocale().toBitRateString(
|
||||||
|
(aggrGuidStats_.value(guid).txBytes
|
||||||
|
+ 24 * aggrGuidStats_.value(guid).txPkts) * 8
|
||||||
|
/ aggrGuidStats_.value(guid).txDuration);
|
||||||
|
case kAvgRxBitRate:
|
||||||
|
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||||
|
XLocale().toBitRateString(
|
||||||
|
(aggrGuidStats_.value(guid).rxBytes
|
||||||
|
+ 24 * aggrGuidStats_.value(guid).rxPkts) * 8
|
||||||
|
/ aggrGuidStats_.value(guid).txDuration);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
@ -214,10 +267,18 @@ void StreamStatsModel::appendStreamStatsList(
|
|||||||
aggrGuid.rxPkts += ss.rxPkts;
|
aggrGuid.rxPkts += ss.rxPkts;
|
||||||
aggrGuid.txPkts += ss.txPkts;
|
aggrGuid.txPkts += ss.txPkts;
|
||||||
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
|
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
|
||||||
|
aggrGuid.rxBytes += ss.rxBytes;
|
||||||
|
aggrGuid.txBytes += ss.txBytes;
|
||||||
|
if (s.tx_duration() > aggrGuid.txDuration)
|
||||||
|
aggrGuid.txDuration = s.tx_duration(); // XXX: use largest or avg?
|
||||||
|
|
||||||
aggrAggr.rxPkts += ss.rxPkts;
|
aggrAggr.rxPkts += ss.rxPkts;
|
||||||
aggrAggr.txPkts += ss.txPkts;
|
aggrAggr.txPkts += ss.txPkts;
|
||||||
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
|
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
|
||||||
|
aggrAggr.rxBytes += ss.rxBytes;
|
||||||
|
aggrAggr.txBytes += ss.txBytes;
|
||||||
|
if (aggrGuid.txDuration > aggrAggr.txDuration)
|
||||||
|
aggrAggr.txDuration = aggrGuid.txDuration;
|
||||||
|
|
||||||
if (!portList_.contains(pgp))
|
if (!portList_.contains(pgp))
|
||||||
portList_.append(pgp);
|
portList_.append(pgp);
|
||||||
@ -225,9 +286,11 @@ void StreamStatsModel::appendStreamStatsList(
|
|||||||
guidList_.append(guid);
|
guidList_.append(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guidList_.size())
|
if (guidList_.size() && !guidList_.contains(kAggrGuid))
|
||||||
guidList_.append(kAggrGuid);
|
guidList_.append(kAggrGuid);
|
||||||
|
|
||||||
|
std::sort(guidList_.begin(), guidList_.end());
|
||||||
|
|
||||||
#if QT_VERSION >= 0x040600
|
#if QT_VERSION >= 0x040600
|
||||||
endResetModel();
|
endResetModel();
|
||||||
#else
|
#else
|
||||||
|
@ -61,7 +61,10 @@ private:
|
|||||||
struct AggrGuidStats {
|
struct AggrGuidStats {
|
||||||
quint64 rxPkts;
|
quint64 rxPkts;
|
||||||
quint64 txPkts;
|
quint64 txPkts;
|
||||||
|
quint64 rxBytes;
|
||||||
|
quint64 txBytes;
|
||||||
qint64 pktLoss;
|
qint64 pktLoss;
|
||||||
|
double txDuration;
|
||||||
};
|
};
|
||||||
QList<Guid> guidList_;
|
QList<Guid> guidList_;
|
||||||
QList<PortGroupPort> portList_;
|
QList<PortGroupPort> portList_;
|
||||||
|
@ -31,7 +31,7 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
|||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
streamStats->addAction(actionShowByteCounters);
|
streamStats->addAction(actionShowDetails);
|
||||||
|
|
||||||
if (id)
|
if (id)
|
||||||
setWindowTitle(windowTitle() + QString("(%1)").arg(id));
|
setWindowTitle(windowTitle() + QString("(%1)").arg(id));
|
||||||
@ -39,7 +39,7 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
|||||||
count++;
|
count++;
|
||||||
|
|
||||||
filterModel_ = new StreamStatsFilterModel(this);
|
filterModel_ = new StreamStatsFilterModel(this);
|
||||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_));
|
||||||
filterModel_->setSourceModel(model);
|
filterModel_->setSourceModel(model);
|
||||||
streamStats->setModel(filterModel_);
|
streamStats->setModel(filterModel_);
|
||||||
|
|
||||||
@ -56,10 +56,10 @@ StreamStatsWindow::~StreamStatsWindow()
|
|||||||
id = 0;
|
id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamStatsWindow::on_actionShowByteCounters_triggered(bool checked)
|
void StreamStatsWindow::on_actionShowDetails_triggered(bool checked)
|
||||||
{
|
{
|
||||||
if (checked)
|
if (checked)
|
||||||
filterModel_->setFilterRegExp(QRegExp(".*"));
|
filterModel_->setFilterRegExp(QRegExp(".*"));
|
||||||
else
|
else
|
||||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_));
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,10 @@ public:
|
|||||||
~StreamStatsWindow();
|
~StreamStatsWindow();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_actionShowByteCounters_triggered(bool checked);
|
void on_actionShowDetails_triggered(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString kDefaultFilter_{"^(?!Port).*"};
|
||||||
QSortFilterProxyModel *filterModel_;
|
QSortFilterProxyModel *filterModel_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,12 +27,12 @@ Wait a little bit to see if they appear, otherwise verify your stream stats conf
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
<action name="actionShowByteCounters">
|
<action name="actionShowDetails">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Byte Counters</string>
|
<string>Show Details</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
|
510
client/streamswidget.cpp
Normal file
510
client/streamswidget.cpp
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 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 "streamswidget.h"
|
||||||
|
|
||||||
|
#include "clipboardhelper.h"
|
||||||
|
#include "findreplace.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "streamconfigdialog.h"
|
||||||
|
#include "streamfileformat.h"
|
||||||
|
#include "streamlistdelegate.h"
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QItemSelectionModel>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
extern ClipboardHelper *clipboardHelper;
|
||||||
|
|
||||||
|
StreamsWidget::StreamsWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
delegate = new StreamListDelegate;
|
||||||
|
tvStreamList->setItemDelegate(delegate);
|
||||||
|
|
||||||
|
tvStreamList->verticalHeader()->setDefaultSectionSize(
|
||||||
|
tvStreamList->verticalHeader()->minimumSectionSize());
|
||||||
|
|
||||||
|
// Populate StreamList Context Menu Actions
|
||||||
|
tvStreamList->addAction(actionNew_Stream);
|
||||||
|
tvStreamList->addAction(actionEdit_Stream);
|
||||||
|
tvStreamList->addAction(actionDuplicate_Stream);
|
||||||
|
tvStreamList->addAction(actionDelete_Stream);
|
||||||
|
|
||||||
|
QAction *sep2 = new QAction(this);
|
||||||
|
sep2->setSeparator(true);
|
||||||
|
tvStreamList->addAction(sep2);
|
||||||
|
|
||||||
|
tvStreamList->addAction(actionFind_Replace);
|
||||||
|
|
||||||
|
QAction *sep3 = new QAction(this);
|
||||||
|
sep3->setSeparator(true);
|
||||||
|
tvStreamList->addAction(sep3);
|
||||||
|
|
||||||
|
tvStreamList->addAction(actionOpen_Streams);
|
||||||
|
tvStreamList->addAction(actionSave_Streams);
|
||||||
|
|
||||||
|
// StreamWidget's actions is an aggegate of all sub-widget's actions
|
||||||
|
addActions(tvStreamList->actions());
|
||||||
|
|
||||||
|
// Add the clipboard actions to the context menu of streamList
|
||||||
|
// but not to StreamsWidget's actions since they are already available
|
||||||
|
// in the global Edit Menu
|
||||||
|
QAction *sep4 = new QAction("Clipboard", this);
|
||||||
|
sep4->setSeparator(true);
|
||||||
|
tvStreamList->insertAction(sep2, sep4);
|
||||||
|
tvStreamList->insertActions(sep2, clipboardHelper->actions());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::setPortGroupList(PortGroupList *portGroups)
|
||||||
|
{
|
||||||
|
plm = portGroups;
|
||||||
|
|
||||||
|
tvStreamList->setModel(plm->getStreamModel());
|
||||||
|
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
|
||||||
|
connect(tvStreamList->selectionModel(),
|
||||||
|
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
connect(tvStreamList->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
|
||||||
|
tvStreamList->resizeColumnToContents(StreamModel::StreamIcon);
|
||||||
|
tvStreamList->resizeColumnToContents(StreamModel::StreamStatus);
|
||||||
|
|
||||||
|
updateStreamViewActions();
|
||||||
|
|
||||||
|
connect(plm->getStreamModel(),
|
||||||
|
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(streamModelDataChanged()));
|
||||||
|
connect(plm->getStreamModel(),
|
||||||
|
SIGNAL(modelReset()),
|
||||||
|
this, SLOT(streamModelDataChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::streamModelDataChanged()
|
||||||
|
{
|
||||||
|
if (plm->isPort(currentPortIndex_))
|
||||||
|
plm->port(currentPortIndex_).recalculateAverageRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamsWidget::~StreamsWidget()
|
||||||
|
{
|
||||||
|
delete delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_tvStreamList_activated(const QModelIndex & index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qDebug("%s: invalid index", __FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("stream list activated\n");
|
||||||
|
|
||||||
|
Port &curPort = plm->port(currentPortIndex_);
|
||||||
|
|
||||||
|
QList<Stream*> streams;
|
||||||
|
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
if (scd.exec() == QDialog::Accepted) {
|
||||||
|
curPort.recalculateAverageRates();
|
||||||
|
curPort.setLocalConfigChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::setCurrentPortIndex(const QModelIndex &portIndex)
|
||||||
|
{
|
||||||
|
if (!plm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel
|
||||||
|
if (!plm->isPort(portIndex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
currentPortIndex_ = portIndex;
|
||||||
|
plm->getStreamModel()->setCurrentPortIndex(portIndex);
|
||||||
|
|
||||||
|
updateStreamViewActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::updateStreamViewActions()
|
||||||
|
{
|
||||||
|
// For some reason hasSelection() returns true even if selection size is 0
|
||||||
|
// so additional check for size introduced
|
||||||
|
if (tvStreamList->selectionModel()->hasSelection() &&
|
||||||
|
(tvStreamList->selectionModel()->selection().size() > 0))
|
||||||
|
{
|
||||||
|
qDebug("Has selection %d",
|
||||||
|
tvStreamList->selectionModel()->selection().size());
|
||||||
|
|
||||||
|
// If more than one non-contiguous ranges selected,
|
||||||
|
// disable "New" and "Edit"
|
||||||
|
if (tvStreamList->selectionModel()->selection().size() > 1)
|
||||||
|
{
|
||||||
|
actionNew_Stream->setDisabled(true);
|
||||||
|
actionEdit_Stream->setDisabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actionNew_Stream->setEnabled(true);
|
||||||
|
actionEdit_Stream->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate/Delete are always enabled as long as we have a selection
|
||||||
|
actionDuplicate_Stream->setEnabled(true);
|
||||||
|
actionDelete_Stream->setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug("No selection");
|
||||||
|
if (plm->isPort(currentPortIndex_))
|
||||||
|
actionNew_Stream->setEnabled(true);
|
||||||
|
else
|
||||||
|
actionNew_Stream->setDisabled(true);
|
||||||
|
actionEdit_Stream->setDisabled(true);
|
||||||
|
actionDuplicate_Stream->setDisabled(true);
|
||||||
|
actionDelete_Stream->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionFind_Replace->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||||
|
|
||||||
|
actionOpen_Streams->setEnabled(plm->isPort(currentPortIndex_));
|
||||||
|
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionNew_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("New Stream Action");
|
||||||
|
|
||||||
|
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||||
|
if (selectionModel->selection().size() > 1) {
|
||||||
|
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
|
||||||
|
selectionModel->selection().size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case nothing is selected, insert 1 row at the end
|
||||||
|
StreamModel *streamModel = plm->getStreamModel();
|
||||||
|
int row = streamModel->rowCount(), count = 1;
|
||||||
|
|
||||||
|
// In case we have a single range selected; insert as many rows as
|
||||||
|
// in the singe selected range before the top of the selected range
|
||||||
|
if (selectionModel->selection().size() == 1)
|
||||||
|
{
|
||||||
|
row = selectionModel->selection().at(0).top();
|
||||||
|
count = selectionModel->selection().at(0).height();
|
||||||
|
}
|
||||||
|
|
||||||
|
Port &curPort = plm->port(currentPortIndex_);
|
||||||
|
|
||||||
|
QList<Stream*> streams;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
streams.append(new Stream);
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
scd.setWindowTitle(tr("Add Stream"));
|
||||||
|
if (scd.exec() == QDialog::Accepted)
|
||||||
|
streamModel->insert(row, streams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionEdit_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Edit Stream Action");
|
||||||
|
|
||||||
|
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
|
||||||
|
if (!streamModel->hasSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Port &curPort = plm->port(currentPortIndex_);
|
||||||
|
|
||||||
|
QList<Stream*> streams;
|
||||||
|
foreach(QModelIndex index, streamModel->selectedRows())
|
||||||
|
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
if (scd.exec() == QDialog::Accepted) {
|
||||||
|
curPort.recalculateAverageRates();
|
||||||
|
curPort.setLocalConfigChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionDuplicate_Stream_triggered()
|
||||||
|
{
|
||||||
|
QItemSelectionModel* model = tvStreamList->selectionModel();
|
||||||
|
|
||||||
|
qDebug("Duplicate Stream Action");
|
||||||
|
|
||||||
|
if (model->hasSelection())
|
||||||
|
{
|
||||||
|
bool isOk;
|
||||||
|
int count = QInputDialog::getInt(this, "Duplicate Streams",
|
||||||
|
"Count", 1, 1, 9999, 1, &isOk);
|
||||||
|
|
||||||
|
if (!isOk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QList<int> list;
|
||||||
|
foreach(QModelIndex index, model->selectedRows())
|
||||||
|
list.append(index.row());
|
||||||
|
plm->port(currentPortIndex_).duplicateStreams(list, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug("No selection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionDelete_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Delete Stream Action");
|
||||||
|
|
||||||
|
QModelIndex index;
|
||||||
|
|
||||||
|
if (tvStreamList->selectionModel()->hasSelection())
|
||||||
|
{
|
||||||
|
qDebug("SelectedIndexes %d",
|
||||||
|
tvStreamList->selectionModel()->selectedRows().size());
|
||||||
|
while(tvStreamList->selectionModel()->selectedRows().size())
|
||||||
|
{
|
||||||
|
index = tvStreamList->selectionModel()->selectedRows().at(0);
|
||||||
|
plm->getStreamModel()->removeRows(index.row(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug("No selection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionFind_Replace_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Find & Replace Action");
|
||||||
|
|
||||||
|
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||||
|
FindReplaceDialog::Action action;
|
||||||
|
|
||||||
|
action.selectedStreamsOnly = selectionModel->selection().size() > 0 ?
|
||||||
|
true : false;
|
||||||
|
|
||||||
|
FindReplaceDialog findReplace(&action, this);
|
||||||
|
if (findReplace.exec() == QDialog::Accepted) {
|
||||||
|
QProgressDialog progress(this);
|
||||||
|
progress.setLabelText(tr("Replacing %1 ...").arg(action.protocolField));
|
||||||
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
|
int c, fc = 0, sc = 0; // replace counts
|
||||||
|
Port &port = plm->port(currentPortIndex_);
|
||||||
|
if (action.selectedStreamsOnly) {
|
||||||
|
QModelIndexList selected = selectionModel->selectedRows();
|
||||||
|
int count = selected.size();
|
||||||
|
progress.setMaximum(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
QModelIndex index = selected.at(i);
|
||||||
|
Stream *stream = port.mutableStreamByIndex(index.row(), false);
|
||||||
|
c = stream->protocolFieldReplace(action.protocolNumber,
|
||||||
|
action.fieldIndex,
|
||||||
|
action.fieldBitSize,
|
||||||
|
action.findValue,
|
||||||
|
action.findMask,
|
||||||
|
action.replaceValue,
|
||||||
|
action.replaceMask);
|
||||||
|
if (c) {
|
||||||
|
fc += c;
|
||||||
|
sc++;
|
||||||
|
}
|
||||||
|
progress.setValue(i+1);
|
||||||
|
if (progress.wasCanceled())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int count = tvStreamList->model()->rowCount();
|
||||||
|
progress.setMaximum(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Stream *stream = port.mutableStreamByIndex(i, false);
|
||||||
|
c = stream->protocolFieldReplace(action.protocolNumber,
|
||||||
|
action.fieldIndex,
|
||||||
|
action.fieldBitSize,
|
||||||
|
action.findValue,
|
||||||
|
action.findMask,
|
||||||
|
action.replaceValue,
|
||||||
|
action.replaceMask);
|
||||||
|
if (c) {
|
||||||
|
fc += c;
|
||||||
|
sc++;
|
||||||
|
}
|
||||||
|
progress.setValue(i+1);
|
||||||
|
if (progress.wasCanceled())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fc)
|
||||||
|
port.setLocalConfigChanged(true);
|
||||||
|
|
||||||
|
QMessageBox::information(this, tr("Find & Replace"),
|
||||||
|
tr("%1 fields replaced in %2 streams").arg(fc).arg(sc));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionOpen_Streams_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Open Streams Action");
|
||||||
|
|
||||||
|
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
|
||||||
|
StreamFileFormat::kOpenFile);
|
||||||
|
QString fileType;
|
||||||
|
static QString dirName;
|
||||||
|
QString fileName;
|
||||||
|
QString errorStr;
|
||||||
|
bool append = true;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
if (fileTypes.size())
|
||||||
|
fileType = fileTypes.at(0);
|
||||||
|
|
||||||
|
fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"),
|
||||||
|
dirName, fileTypes.join(";;"), &fileType);
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (tvStreamList->model()->rowCount())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(),
|
||||||
|
tr("Append to existing streams? Or overwrite?"),
|
||||||
|
QMessageBox::NoButton, this);
|
||||||
|
QPushButton *appendBtn = msgBox.addButton(tr("Append"),
|
||||||
|
QMessageBox::ActionRole);
|
||||||
|
QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"),
|
||||||
|
QMessageBox::ActionRole);
|
||||||
|
QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
if (msgBox.clickedButton() == cancelBtn)
|
||||||
|
goto _exit;
|
||||||
|
else if (msgBox.clickedButton() == appendBtn)
|
||||||
|
append = true;
|
||||||
|
else if (msgBox.clickedButton() == overwriteBtn)
|
||||||
|
append = false;
|
||||||
|
else
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = plm->port(currentPortIndex_).openStreams(fileName, append, errorStr);
|
||||||
|
if (!ret || !errorStr.isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(this);
|
||||||
|
QStringList str = errorStr.split("\n\n\n\n");
|
||||||
|
|
||||||
|
msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical);
|
||||||
|
msgBox.setWindowTitle(qApp->applicationName());
|
||||||
|
msgBox.setText(str.at(0));
|
||||||
|
if (str.size() > 1)
|
||||||
|
msgBox.setDetailedText(str.at(1));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
dirName = QFileInfo(fileName).absolutePath();
|
||||||
|
updateStreamViewActions();
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamsWidget::on_actionSave_Streams_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Save Streams Action");
|
||||||
|
|
||||||
|
static QString fileName;
|
||||||
|
QStringList fileTypes = StreamFileFormat::supportedFileTypes(
|
||||||
|
StreamFileFormat::kSaveFile);
|
||||||
|
QString fileType;
|
||||||
|
QString errorStr;
|
||||||
|
QFileDialog::Options options;
|
||||||
|
|
||||||
|
// On Mac OS with Native Dialog, getSaveFileName() ignores fileType
|
||||||
|
// which we need
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
options |= QFileDialog::DontUseNativeDialog;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fileTypes.size())
|
||||||
|
fileType = fileTypes.at(0);
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||||
|
|
||||||
|
_retry:
|
||||||
|
fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"),
|
||||||
|
fileName, fileTypes.join(";;"), &fileType, options);
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (QFileInfo(fileName).suffix().isEmpty()) {
|
||||||
|
QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1);
|
||||||
|
qDebug("Adding extension '%s' to '%s'",
|
||||||
|
qPrintable(fileExt), qPrintable(fileName));
|
||||||
|
fileName.append(fileExt);
|
||||||
|
if (QFileInfo(fileName).exists()) {
|
||||||
|
if (QMessageBox::warning(this, tr("Overwrite File?"),
|
||||||
|
QString("The file \"%1\" already exists.\n\n"
|
||||||
|
"Do you wish to overwrite it?")
|
||||||
|
.arg(QFileInfo(fileName).fileName()),
|
||||||
|
QMessageBox::Yes|QMessageBox::No,
|
||||||
|
QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
goto _retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed();
|
||||||
|
if (!fileType.startsWith("Ostinato")
|
||||||
|
&& !fileType.startsWith("Python"))
|
||||||
|
{
|
||||||
|
if (QMessageBox::warning(this, tr("Ostinato"),
|
||||||
|
QString("You have chosen to save in %1 format. All stream "
|
||||||
|
"attributes may not be saved in this format.\n\n"
|
||||||
|
"It is recommended to save in native Ostinato format.\n\n"
|
||||||
|
"Continue to save in %2 format?").arg(fileType).arg(fileType),
|
||||||
|
QMessageBox::Yes|QMessageBox::No,
|
||||||
|
QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
goto _retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: all or selected?
|
||||||
|
|
||||||
|
if (!plm->port(currentPortIndex_).saveStreams(fileName, fileType, errorStr))
|
||||||
|
QMessageBox::critical(this, qApp->applicationName(), errorStr);
|
||||||
|
else if (!errorStr.isEmpty())
|
||||||
|
QMessageBox::warning(this, qApp->applicationName(), errorStr);
|
||||||
|
|
||||||
|
fileName = QFileInfo(fileName).absolutePath();
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
67
client/streamswidget.h
Normal file
67
client/streamswidget.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 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 _STREAMS_WIDGET_H
|
||||||
|
#define _STREAMS_WIDGET_H
|
||||||
|
|
||||||
|
#include "ui_streamswidget.h"
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class PortGroupList;
|
||||||
|
class QAbstractItemDelegate;
|
||||||
|
|
||||||
|
class StreamsWidget : public QWidget, private Ui::StreamsWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
StreamsWidget(QWidget *parent = 0);
|
||||||
|
~StreamsWidget();
|
||||||
|
|
||||||
|
void setPortGroupList(PortGroupList *portGroups);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setCurrentPortIndex(const QModelIndex &portIndex);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateStreamViewActions();
|
||||||
|
|
||||||
|
void on_tvStreamList_activated(const QModelIndex & index);
|
||||||
|
|
||||||
|
void on_actionNew_Stream_triggered();
|
||||||
|
void on_actionEdit_Stream_triggered();
|
||||||
|
void on_actionDuplicate_Stream_triggered();
|
||||||
|
void on_actionDelete_Stream_triggered();
|
||||||
|
|
||||||
|
void on_actionFind_Replace_triggered();
|
||||||
|
|
||||||
|
void on_actionOpen_Streams_triggered();
|
||||||
|
void on_actionSave_Streams_triggered();
|
||||||
|
|
||||||
|
void streamModelDataChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortGroupList *plm{nullptr}; // FIXME: rename to portGroups_?
|
||||||
|
QModelIndex currentPortIndex_;
|
||||||
|
|
||||||
|
QAbstractItemDelegate *delegate;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
132
client/streamswidget.ui
Normal file
132
client/streamswidget.ui
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>StreamsWidget</class>
|
||||||
|
<widget class="QWidget" name="StreamsWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>602</width>
|
||||||
|
<height>364</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="XTableView" name="tvStreamList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>This is the stream list for the selected port
|
||||||
|
|
||||||
|
A stream is a sequence of one or more packets
|
||||||
|
|
||||||
|
Right-click to create a stream</string>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
<action name="actionNew_Stream">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>New Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDelete_Stream">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionEdit_Stream">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionOpen_Streams">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open Streams ...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSave_Streams">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save Streams ...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDuplicate_Stream">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Duplicate Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionFind_Replace">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Find && Replace</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Find & Replace protocol field values</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>XTableView</class>
|
||||||
|
<extends>QTableView</extends>
|
||||||
|
<header>xtableview.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
130
client/thememanager.cpp
Normal file
130
client/thememanager.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "thememanager.h"
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QResource>
|
||||||
|
|
||||||
|
ThemeManager *ThemeManager::instance_{nullptr};
|
||||||
|
|
||||||
|
ThemeManager::ThemeManager()
|
||||||
|
{
|
||||||
|
themeDir_ = QCoreApplication::applicationDirPath() + "/themes/";
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
/*
|
||||||
|
* Executable and Theme directory location inside app bundle -
|
||||||
|
* Ostinato.app/Contents/MacOS/
|
||||||
|
* Ostinato.app/Contents/SharedSupport/themes/
|
||||||
|
*/
|
||||||
|
themeDir_.replace("/MacOS/", "/SharedSupport/");
|
||||||
|
#elif defined(Q_OS_UNIX)
|
||||||
|
/*
|
||||||
|
* Possible (but not comprehensive) locations for Ostinato executable
|
||||||
|
* and theme directory locations
|
||||||
|
*
|
||||||
|
* non-install-dir/
|
||||||
|
* non-install-dir/themes/
|
||||||
|
*
|
||||||
|
* /usr/[local]/bin/
|
||||||
|
* /usr/[local]/share/ostinato/themes/
|
||||||
|
*
|
||||||
|
* /opt/ostinato/bin/
|
||||||
|
* /opt/ostinato/share/themes/
|
||||||
|
*/
|
||||||
|
if (themeDir_.contains(QRegularExpression("^/usr/.*/bin/")))
|
||||||
|
themeDir_.replace("/bin/", "/share/ostinato/");
|
||||||
|
else if (themeDir_.contains(QRegularExpression("^/opt/.*/bin/")))
|
||||||
|
themeDir_.replace("/bin/", "/share/");
|
||||||
|
#endif
|
||||||
|
qDebug("Themes directory: %s", qPrintable(themeDir_));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ThemeManager::themeList()
|
||||||
|
{
|
||||||
|
QDir themeDir(themeDir_);
|
||||||
|
|
||||||
|
themeDir.setFilter(QDir::Files);
|
||||||
|
themeDir.setNameFilters(QStringList() << "*.qss");
|
||||||
|
themeDir.setSorting(QDir::Name);
|
||||||
|
|
||||||
|
QStringList themes = themeDir.entryList();
|
||||||
|
for (QString& theme : themes)
|
||||||
|
theme.remove(".qss");
|
||||||
|
themes.prepend("default");
|
||||||
|
|
||||||
|
return themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::setTheme(QString theme)
|
||||||
|
{
|
||||||
|
// Remove current theme, if we have one
|
||||||
|
QString oldTheme = appSettings->value(kThemeKey).toString();
|
||||||
|
if (!oldTheme.isEmpty()) {
|
||||||
|
// Remove stylesheet first so that there are
|
||||||
|
// no references to resources when unregistering 'em
|
||||||
|
qApp->setStyleSheet("");
|
||||||
|
QString rccFile = themeDir_ + oldTheme + ".rcc";
|
||||||
|
if (QResource::unregisterResource(rccFile)) {
|
||||||
|
qDebug("Unable to unregister theme rccFile %s",
|
||||||
|
qPrintable(rccFile));
|
||||||
|
}
|
||||||
|
appSettings->setValue(kThemeKey, QVariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.isEmpty() || (theme == "default"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Apply new theme
|
||||||
|
QFile qssFile(themeDir_ + theme + ".qss");
|
||||||
|
if (!qssFile.open(QFile::ReadOnly)) {
|
||||||
|
qDebug("Unable to open theme qssFile %s",
|
||||||
|
qPrintable(qssFile.fileName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register theme resource before applying theme style sheet
|
||||||
|
QString rccFile = themeDir_ + theme + ".rcc";
|
||||||
|
if (!QResource::registerResource(rccFile))
|
||||||
|
qDebug("Unable to register theme rccFile %s", qPrintable(rccFile));
|
||||||
|
|
||||||
|
#if 0 // FIXME: debug only
|
||||||
|
QDirIterator it(":", QDirIterator::Subdirectories);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
qDebug() << it.next();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString styleSheet { qssFile.readAll() };
|
||||||
|
qApp->setStyleSheet(styleSheet);
|
||||||
|
appSettings->setValue(kThemeKey, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeManager* ThemeManager::instance()
|
||||||
|
{
|
||||||
|
if (!instance_)
|
||||||
|
instance_ = new ThemeManager();
|
||||||
|
return instance_;
|
||||||
|
}
|
42
client/thememanager.h
Normal file
42
client/thememanager.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _THEME_MANAGER_H
|
||||||
|
#define _THEME_MANAGER_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class ThemeManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ThemeManager();
|
||||||
|
|
||||||
|
QStringList themeList();
|
||||||
|
void setTheme(QString theme);
|
||||||
|
|
||||||
|
static ThemeManager* instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString themeDir_;
|
||||||
|
|
||||||
|
static ThemeManager *instance_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
1307
client/themes/material-dark.qss
Normal file
1307
client/themes/material-dark.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/material-dark.rcc
Normal file
BIN
client/themes/material-dark.rcc
Normal file
Binary file not shown.
1307
client/themes/material-light.qss
Normal file
1307
client/themes/material-light.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/material-light.rcc
Normal file
BIN
client/themes/material-light.rcc
Normal file
Binary file not shown.
2213
client/themes/qds-dark.qss
Normal file
2213
client/themes/qds-dark.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/qds-dark.rcc
Normal file
BIN
client/themes/qds-dark.rcc
Normal file
Binary file not shown.
2213
client/themes/qds-light.qss
Normal file
2213
client/themes/qds-light.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/qds-light.rcc
Normal file
BIN
client/themes/qds-light.rcc
Normal file
Binary file not shown.
@ -376,6 +376,9 @@ void VariableFieldsWidget::loadProtocolFields(
|
|||||||
int byteOfs = bitOfs >> 3;
|
int byteOfs = bitOfs >> 3;
|
||||||
uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
||||||
.toInt();
|
.toInt();
|
||||||
|
if (bitSize == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
vm["offset"] = byteOfs;
|
vm["offset"] = byteOfs;
|
||||||
if (bitSize <= 8) {
|
if (bitSize <= 8) {
|
||||||
vm["type"] = int(OstProto::VariableField::kCounter8);
|
vm["type"] = int(OstProto::VariableField::kCounter8);
|
||||||
|
@ -21,14 +21,85 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#define _X_LOCALE_H
|
#define _X_LOCALE_H
|
||||||
|
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
|
||||||
class XLocale: public QLocale
|
class XLocale: public QLocale
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
double toDouble(const QString &s, bool *ok = Q_NULLPTR) const {
|
double toDouble(const QString &s, bool *ok = Q_NULLPTR) const
|
||||||
|
{
|
||||||
QString s2 = s;
|
QString s2 = s;
|
||||||
return QLocale::toDouble(s2.remove(groupSeparator()), ok);
|
return QLocale::toDouble(s2.remove(groupSeparator()), ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double toPacketsPerSecond(const QString &s, bool *ok = Q_NULLPTR) const
|
||||||
|
{
|
||||||
|
QString text = s;
|
||||||
|
double multiplier = 0;
|
||||||
|
QRegularExpression regex("[a-zA-Z/]+$");
|
||||||
|
QRegularExpressionMatch match = regex.match(text);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
QString unit = match.captured(0).toCaseFolded();
|
||||||
|
if ((unit == "mpps") || (unit == "m"))
|
||||||
|
multiplier = 1e6;
|
||||||
|
else if ((unit == "kpps") || (unit == "k"))
|
||||||
|
multiplier = 1e3;
|
||||||
|
else if (unit == "pps")
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
if (multiplier)
|
||||||
|
text.remove(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiplier == 0)
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
return toDouble(text, ok) * multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
double toBitsPerSecond(const QString &s, bool *ok = Q_NULLPTR) const
|
||||||
|
{
|
||||||
|
QString text = s;
|
||||||
|
double multiplier = 0;
|
||||||
|
QRegularExpression regex("[a-zA-Z/]+$");
|
||||||
|
QRegularExpressionMatch match = regex.match(text);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
QString unit = match.captured(0).toCaseFolded();
|
||||||
|
if ((unit == "gbps") || (unit == "gb/s") || (unit == "g"))
|
||||||
|
multiplier = 1e9;
|
||||||
|
else if ((unit == "mbps") || (unit == "mb/s") || (unit == "m"))
|
||||||
|
multiplier = 1e6;
|
||||||
|
else if ((unit == "kbps") || (unit == "kb/s") || (unit == "k"))
|
||||||
|
multiplier = 1e3;
|
||||||
|
else if ((unit == "bps") || (unit == "b/s"))
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
if (multiplier)
|
||||||
|
text.remove(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiplier == 0)
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
return toDouble(text, ok) * multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toBitRateString(double bps) const
|
||||||
|
{
|
||||||
|
QString text;
|
||||||
|
|
||||||
|
if (bps >= 1e9)
|
||||||
|
return QObject::tr("%L1 Gbps").arg(bps/1e9, 0, 'f', 4);
|
||||||
|
|
||||||
|
if (bps >= 1e6)
|
||||||
|
return QObject::tr("%L1 Mbps").arg(bps/1e6, 0, 'f', 4);
|
||||||
|
|
||||||
|
if (bps >= 1e3)
|
||||||
|
return QObject::tr("%L1 Kbps").arg(bps/1e3, 0, 'f', 4);
|
||||||
|
|
||||||
|
return QObject::tr("%L1 bps").arg(bps, 0, 'f', 4);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -857,7 +857,7 @@ bool AbstractProtocol::protocolHasPayload() const
|
|||||||
to prevent infinite recursion
|
to prevent infinite recursion
|
||||||
*/
|
*/
|
||||||
quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
|
quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
|
||||||
CksumType cksumType) const
|
CksumType cksumType, CksumFlags cksumFlags) const
|
||||||
{
|
{
|
||||||
static int recursionCount = 0;
|
static int recursionCount = 0;
|
||||||
quint32 cksum = 0xFFFFFFFF;
|
quint32 cksum = 0xFFFFFFFF;
|
||||||
@ -872,8 +872,10 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
|
|||||||
QByteArray fv;
|
QByteArray fv;
|
||||||
quint16 *ip;
|
quint16 *ip;
|
||||||
quint32 len, sum = 0;
|
quint32 len, sum = 0;
|
||||||
|
bool forCksum = cksumFlags.testFlag(IncludeCksumField) ?
|
||||||
|
false : true;
|
||||||
|
|
||||||
fv = protocolFrameValue(streamIndex, true);
|
fv = protocolFrameValue(streamIndex, forCksum);
|
||||||
ip = (quint16*) fv.constData();
|
ip = (quint16*) fv.constData();
|
||||||
len = fv.size();
|
len = fv.size();
|
||||||
|
|
||||||
@ -914,7 +916,26 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
|
|||||||
cksum = (~sum) & 0xFFFF;
|
cksum = (~sum) & 0xFFFF;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CksumIcmpIgmp:
|
||||||
|
{
|
||||||
|
quint16 cks;
|
||||||
|
quint32 sum = 0;
|
||||||
|
|
||||||
|
cks = protocolFrameCksum(streamIndex, CksumIp);
|
||||||
|
sum += (quint16) ~cks;
|
||||||
|
cks = protocolFramePayloadCksum(streamIndex, CksumIp);
|
||||||
|
sum += (quint16) ~cks;
|
||||||
|
|
||||||
|
while(sum>>16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
cksum = (~sum) & 0xFFFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
qDebug("Unknown cksumType %d", cksumType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1001,7 +1022,7 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex,
|
|||||||
if (!p)
|
if (!p)
|
||||||
return 0xFFFF;
|
return 0xFFFF;
|
||||||
|
|
||||||
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
cksum = p->protocolFrameCksum(streamIndex, cksumType, IncludeCksumField);
|
||||||
sum = (quint16) ~cksum;
|
sum = (quint16) ~cksum;
|
||||||
if (cksumScope == CksumScopeAdjacentProtocol)
|
if (cksumScope == CksumScopeAdjacentProtocol)
|
||||||
goto out;
|
goto out;
|
||||||
@ -1009,9 +1030,9 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex,
|
|||||||
p = p->next;
|
p = p->next;
|
||||||
while (p)
|
while (p)
|
||||||
{
|
{
|
||||||
|
cksum = p->protocolFrameCksum(streamIndex, cksumType, IncludeCksumField);
|
||||||
// when combining cksums, a non-first protocol starting at odd offset
|
// when combining cksums, a non-first protocol starting at odd offset
|
||||||
// needs a byte swap (see RFC 1071 section(s) 2A, 2B)
|
// needs a byte swap (see RFC 1071 section(s) 2A, 2B)
|
||||||
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
|
||||||
if (p->protocolFrameOffset(streamIndex) & 0x1)
|
if (p->protocolFrameOffset(streamIndex) & 0x1)
|
||||||
cksum = swap16(cksum);
|
cksum = swap16(cksum);
|
||||||
sum += (quint16) ~cksum;
|
sum += (quint16) ~cksum;
|
||||||
|
@ -102,10 +102,17 @@ public:
|
|||||||
CksumIp, //!< Standard IP Checksum
|
CksumIp, //!< Standard IP Checksum
|
||||||
CksumIpPseudo, //!< Standard checksum for Pseudo-IP header
|
CksumIpPseudo, //!< Standard checksum for Pseudo-IP header
|
||||||
CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP
|
CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP
|
||||||
|
CksumIcmpIgmp, //!< Standard ICMP/IGMP checksum
|
||||||
|
|
||||||
CksumMax //!< Marker for number of cksum types
|
CksumMax //!< Marker for number of cksum types
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Flags affecting cksum calculation, can be OR'd
|
||||||
|
enum CksumFlag {
|
||||||
|
IncludeCksumField = 0x1, //!< Default: exclude cksum field(s)
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(CksumFlags, CksumFlag); //!< \private abcd
|
||||||
|
|
||||||
//! Supported checksum scopes
|
//! Supported checksum scopes
|
||||||
enum CksumScope {
|
enum CksumScope {
|
||||||
CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol
|
CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol
|
||||||
@ -164,7 +171,7 @@ public:
|
|||||||
bool protocolHasPayload() const;
|
bool protocolHasPayload() const;
|
||||||
|
|
||||||
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
||||||
CksumType cksumType = CksumIp) const;
|
CksumType cksumType = CksumIp, CksumFlags cksumFlags = 0) const;
|
||||||
quint32 protocolFrameHeaderCksum(int streamIndex = 0,
|
quint32 protocolFrameHeaderCksum(int streamIndex = 0,
|
||||||
CksumType cksumType = CksumIp,
|
CksumType cksumType = CksumIp,
|
||||||
CksumScope cksumScope = CksumScopeAdjacentProtocol) const;
|
CksumScope cksumScope = CksumScopeAdjacentProtocol) const;
|
||||||
|
@ -430,7 +430,7 @@ QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
|
|
||||||
switch(data.target_proto_addr_mode())
|
switch(data.target_proto_addr_mode())
|
||||||
{
|
{
|
||||||
case OstProto::Arp::kFixed:
|
case OstProto::Arp::kFixedHost:
|
||||||
protoAddr = data.target_proto_addr();
|
protoAddr = data.target_proto_addr();
|
||||||
break;
|
break;
|
||||||
case OstProto::Arp::kIncrementHost:
|
case OstProto::Arp::kIncrementHost:
|
||||||
@ -786,7 +786,7 @@ int ArpProtocol::protocolFrameVariableCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()
|
if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()
|
||||||
!= uint(OstProto::Arp::kFixed))
|
!= uint(OstProto::Arp::kFixedHost))
|
||||||
{
|
{
|
||||||
count = AbstractProtocol::lcm(count,
|
count = AbstractProtocol::lcm(count,
|
||||||
fieldData(arp_senderProtoAddrCount, FieldValue).toUInt());
|
fieldData(arp_senderProtoAddrCount, FieldValue).toUInt());
|
||||||
@ -800,7 +800,7 @@ int ArpProtocol::protocolFrameVariableCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()
|
if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()
|
||||||
!= uint(OstProto::Arp::kFixed))
|
!= uint(OstProto::Arp::kFixedHost))
|
||||||
{
|
{
|
||||||
count = AbstractProtocol::lcm(count,
|
count = AbstractProtocol::lcm(count,
|
||||||
fieldData(arp_targetProtoAddrCount, FieldValue).toUInt());
|
fieldData(arp_targetProtoAddrCount, FieldValue).toUInt());
|
||||||
|
@ -168,14 +168,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
||||||
CksumType cksumType = CksumIp) const
|
CksumType cksumType = CksumIp, CksumFlags cksumFlags = 0) const
|
||||||
{
|
{
|
||||||
// For a Pseudo IP cksum, we assume it is the succeeding protocol
|
// For a Pseudo IP cksum, we assume it is the succeeding protocol
|
||||||
// that is requesting it and hence return protoB's cksum;
|
// that is requesting it and hence return protoB's cksum;
|
||||||
if (cksumType == CksumIpPseudo)
|
if (cksumType == CksumIpPseudo)
|
||||||
return protoB->protocolFrameCksum(streamIndex, cksumType);
|
return protoB->protocolFrameCksum(
|
||||||
|
streamIndex,cksumType, cksumFlags);
|
||||||
|
|
||||||
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
|
return AbstractProtocol::protocolFrameCksum(
|
||||||
|
streamIndex, cksumType, cksumFlags);
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
quint32 protocolFrameHeaderCksum(int streamIndex = 0,
|
quint32 protocolFrameHeaderCksum(int streamIndex = 0,
|
||||||
|
@ -671,6 +671,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
ba.append(QByteArray(4 - (ba.size() % 4), char(0)));
|
ba.append(QByteArray(4 - (ba.size() % 4), char(0)));
|
||||||
rec->set_aux_data(std::string(ba.constData(), ba.size()));
|
rec->set_aux_data(std::string(ba.constData(), ba.size()));
|
||||||
}
|
}
|
||||||
|
isOk = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ void GmpConfigForm::loadWidget(AbstractProtocol *proto)
|
|||||||
groupAddress->setText(
|
groupAddress->setText(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
GmpProtocol::kGroupAddress,
|
GmpProtocol::kGroupAddress,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldTextValue
|
||||||
).toString());
|
).toString());
|
||||||
groupMode->setCurrentIndex(
|
groupMode->setCurrentIndex(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
|
471
common/gre.cpp
Normal file
471
common/gre.cpp
Normal file
@ -0,0 +1,471 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "gre.h"
|
||||||
|
|
||||||
|
GreProtocol::GreProtocol(StreamBase *stream, AbstractProtocol *parent)
|
||||||
|
: AbstractProtocol(stream, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GreProtocol::~GreProtocol()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractProtocol* GreProtocol::createInstance(StreamBase *stream,
|
||||||
|
AbstractProtocol *parent)
|
||||||
|
{
|
||||||
|
return new GreProtocol(stream, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 GreProtocol::protocolNumber() const
|
||||||
|
{
|
||||||
|
return OstProto::Protocol::kGreFieldNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const
|
||||||
|
{
|
||||||
|
protocol.MutableExtension(OstProto::gre)->CopyFrom(data);
|
||||||
|
protocol.mutable_protocol_id()->set_id(protocolNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
||||||
|
{
|
||||||
|
if (protocol.protocol_id().id() == protocolNumber() &&
|
||||||
|
protocol.HasExtension(OstProto::gre))
|
||||||
|
data.MergeFrom(protocol.GetExtension(OstProto::gre));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GreProtocol::name() const
|
||||||
|
{
|
||||||
|
return QString("General Routing Encapsulation Protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GreProtocol::shortName() const
|
||||||
|
{
|
||||||
|
return QString("GRE");
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractProtocol::ProtocolIdType GreProtocol::protocolIdType() const
|
||||||
|
{
|
||||||
|
return ProtocolIdEth;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 GreProtocol::protocolId(ProtocolIdType type) const
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case ProtocolIdIp: return 47;
|
||||||
|
default:break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AbstractProtocol::protocolId(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GreProtocol::fieldCount() const
|
||||||
|
{
|
||||||
|
return gre_fieldCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractProtocol::FieldFlags GreProtocol::fieldFlags(int index) const
|
||||||
|
{
|
||||||
|
AbstractProtocol::FieldFlags flags;
|
||||||
|
|
||||||
|
flags = AbstractProtocol::fieldFlags(index);
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case gre_checksum:
|
||||||
|
flags |= CksumField;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case gre_isOverrideChecksum:
|
||||||
|
flags &= ~FrameField;
|
||||||
|
flags |= MetaField;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GreProtocol::fieldData(int index, FieldAttrib attrib,
|
||||||
|
int streamIndex) const
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case gre_flags:
|
||||||
|
{
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldName:
|
||||||
|
return QString("Flags");
|
||||||
|
case FieldValue:
|
||||||
|
return data.flags();
|
||||||
|
case FieldTextValue:
|
||||||
|
{
|
||||||
|
QString fstr;
|
||||||
|
fstr.append("Cksum:");
|
||||||
|
fstr.append(data.flags() & GRE_FLAG_CKSUM ? "Y" : "N");
|
||||||
|
fstr.append(" Key:");
|
||||||
|
fstr.append(data.flags() & GRE_FLAG_KEY ? "Y" : "N");
|
||||||
|
fstr.append(" Seq:");
|
||||||
|
fstr.append(data.flags() & GRE_FLAG_SEQ ? "Y" : "N");
|
||||||
|
return fstr;
|
||||||
|
}
|
||||||
|
case FieldFrameValue:
|
||||||
|
return QByteArray(1, char(data.flags()));
|
||||||
|
case FieldBitSize:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_rsvd0:
|
||||||
|
{
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldName:
|
||||||
|
return QString("Reserved0");
|
||||||
|
case FieldValue:
|
||||||
|
return data.rsvd0();
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("%1").arg(data.rsvd0());
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(2);
|
||||||
|
qToBigEndian(quint16(data.rsvd0()), (uchar*)fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
case FieldBitSize:
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gre_version:
|
||||||
|
{
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldName:
|
||||||
|
return QString("Version");
|
||||||
|
case FieldValue:
|
||||||
|
return data.version();
|
||||||
|
case FieldFrameValue:
|
||||||
|
return QByteArray(1, char(data.version()));
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("%1").arg(data.version());
|
||||||
|
case FieldBitSize:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_protocol:
|
||||||
|
{
|
||||||
|
quint16 protocol = payloadProtocolId(ProtocolIdEth);
|
||||||
|
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldName:
|
||||||
|
return QString("Protocol");
|
||||||
|
case FieldValue:
|
||||||
|
return protocol;
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(2);
|
||||||
|
qToBigEndian(protocol, (uchar*) fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("0x%1").arg(
|
||||||
|
protocol, 4, BASE_HEX, QChar('0'));;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_checksum:
|
||||||
|
{
|
||||||
|
if (attrib == FieldName)
|
||||||
|
return QString("Checksum");
|
||||||
|
|
||||||
|
if ((data.flags() & GRE_FLAG_CKSUM) == 0)
|
||||||
|
{
|
||||||
|
if (attrib == FieldTextValue)
|
||||||
|
return QObject::tr("<not-included>");
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrib == FieldBitSize)
|
||||||
|
return 16;
|
||||||
|
|
||||||
|
quint16 cksum;
|
||||||
|
if (data.is_override_checksum()) {
|
||||||
|
cksum = data.checksum();
|
||||||
|
} else {
|
||||||
|
quint32 sum = 0;
|
||||||
|
cksum = protocolFrameCksum(streamIndex, CksumIp);
|
||||||
|
sum += (quint16) ~cksum;
|
||||||
|
cksum = protocolFramePayloadCksum(streamIndex, CksumIp);
|
||||||
|
sum += (quint16) ~cksum;
|
||||||
|
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
cksum = (~sum) & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
return cksum;
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(2);
|
||||||
|
qToBigEndian(cksum, (uchar*) fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("0x%1").arg(
|
||||||
|
cksum, 4, BASE_HEX, QChar('0'));;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gre_rsvd1:
|
||||||
|
{
|
||||||
|
if (attrib == FieldName)
|
||||||
|
return QString("Reserved1");
|
||||||
|
|
||||||
|
if ((data.flags() & GRE_FLAG_CKSUM) == 0)
|
||||||
|
{
|
||||||
|
if (attrib == FieldTextValue)
|
||||||
|
return QObject::tr("<not-included>");
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
return data.rsvd1();
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("%1").arg(data.rsvd1());
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(2);
|
||||||
|
qToBigEndian((quint16) data.rsvd1(), (uchar*) fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gre_key:
|
||||||
|
{
|
||||||
|
if (attrib == FieldName)
|
||||||
|
return QString("Key");
|
||||||
|
|
||||||
|
if ((data.flags() & GRE_FLAG_KEY) == 0)
|
||||||
|
{
|
||||||
|
if (attrib == FieldTextValue)
|
||||||
|
return QObject::tr("<not-included>");
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
return data.key();
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("0x%1").arg(data.key(), 8, BASE_HEX, QChar('0'));
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(4);
|
||||||
|
qToBigEndian((quint32) data.key(), (uchar*) fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gre_sequence:
|
||||||
|
{
|
||||||
|
if (attrib == FieldName)
|
||||||
|
return QString("Sequence Number");
|
||||||
|
|
||||||
|
if ((data.flags() & GRE_FLAG_SEQ) == 0)
|
||||||
|
{
|
||||||
|
if (attrib == FieldTextValue)
|
||||||
|
return QObject::tr("<not-included>");
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
return data.sequence_num();
|
||||||
|
case FieldTextValue:
|
||||||
|
return QString("%1").arg(data.sequence_num());
|
||||||
|
case FieldFrameValue:
|
||||||
|
{
|
||||||
|
QByteArray fv;
|
||||||
|
fv.resize(4);
|
||||||
|
qToBigEndian((quint32) data.sequence_num(), (uchar*) fv.data());
|
||||||
|
return fv;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta fields
|
||||||
|
case gre_isOverrideChecksum:
|
||||||
|
{
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
return data.is_override_checksum();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
||||||
|
index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AbstractProtocol::fieldData(index, attrib, streamIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GreProtocol::setFieldData(int index, const QVariant &value,
|
||||||
|
FieldAttrib attrib)
|
||||||
|
{
|
||||||
|
bool isOk = false;
|
||||||
|
|
||||||
|
if (attrib != FieldValue)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case gre_flags:
|
||||||
|
{
|
||||||
|
uint flags = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_flags(flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_rsvd0:
|
||||||
|
{
|
||||||
|
uint rsvd0 = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_rsvd0(rsvd0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_version:
|
||||||
|
{
|
||||||
|
uint ver = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_version(ver);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_protocol:
|
||||||
|
{
|
||||||
|
uint proto = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_protocol_type(proto);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_checksum:
|
||||||
|
{
|
||||||
|
uint csum = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_checksum(csum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_isOverrideChecksum:
|
||||||
|
{
|
||||||
|
data.set_is_override_checksum(value.toBool());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_rsvd1:
|
||||||
|
{
|
||||||
|
uint rsvd1 = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_rsvd1(rsvd1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_key:
|
||||||
|
{
|
||||||
|
uint key = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_key(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gre_sequence:
|
||||||
|
{
|
||||||
|
uint seq = value.toUInt(&isOk);
|
||||||
|
if (isOk)
|
||||||
|
data.set_sequence_num(seq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
||||||
|
index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return isOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GreProtocol::protocolFrameSize(int /*streamIndex*/) const
|
||||||
|
{
|
||||||
|
int size = 4; // mandatory fields - flags, rsvd0, version, protocol
|
||||||
|
|
||||||
|
if (data.flags() & GRE_FLAG_CKSUM)
|
||||||
|
size += 4;
|
||||||
|
if (data.flags() & GRE_FLAG_KEY)
|
||||||
|
size += 4;
|
||||||
|
if (data.flags() & GRE_FLAG_SEQ)
|
||||||
|
size += 4;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
97
common/gre.h
Normal file
97
common/gre.h
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _GRE_H
|
||||||
|
#define _GRE_H
|
||||||
|
|
||||||
|
#include "abstractprotocol.h"
|
||||||
|
#include "gre.pb.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
GRE Protocol Frame Format (RFC2890)-
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|C| |K|S| Reserved0 | Ver | Protocol Type |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Checksum (optional) | Reserved1 (Optional) |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Key (optional) |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Sequence Number (Optional) |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
Figures in brackets represent field width in bits
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define GRE_FLAG_CKSUM 0x8
|
||||||
|
#define GRE_FLAG_KEY 0x2
|
||||||
|
#define GRE_FLAG_SEQ 0x1
|
||||||
|
|
||||||
|
class GreProtocol : public AbstractProtocol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum grefield
|
||||||
|
{
|
||||||
|
// Frame Fields
|
||||||
|
gre_flags = 0,
|
||||||
|
gre_rsvd0,
|
||||||
|
gre_version,
|
||||||
|
gre_protocol,
|
||||||
|
gre_checksum,
|
||||||
|
gre_rsvd1,
|
||||||
|
gre_key,
|
||||||
|
gre_sequence,
|
||||||
|
|
||||||
|
// Meta Fields
|
||||||
|
gre_isOverrideChecksum,
|
||||||
|
|
||||||
|
gre_fieldCount
|
||||||
|
};
|
||||||
|
|
||||||
|
GreProtocol(StreamBase *stream, AbstractProtocol *parent = 0);
|
||||||
|
virtual ~GreProtocol();
|
||||||
|
|
||||||
|
static AbstractProtocol* createInstance(StreamBase *stream,
|
||||||
|
AbstractProtocol *parent = 0);
|
||||||
|
virtual quint32 protocolNumber() const;
|
||||||
|
|
||||||
|
virtual void protoDataCopyInto(OstProto::Protocol &protocol) const;
|
||||||
|
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol);
|
||||||
|
|
||||||
|
virtual ProtocolIdType protocolIdType() const;
|
||||||
|
virtual quint32 protocolId(ProtocolIdType type) const;
|
||||||
|
|
||||||
|
virtual QString name() const;
|
||||||
|
virtual QString shortName() const;
|
||||||
|
|
||||||
|
virtual int fieldCount() const;
|
||||||
|
|
||||||
|
virtual AbstractProtocol::FieldFlags fieldFlags(int index) const;
|
||||||
|
virtual QVariant fieldData(int index, FieldAttrib attrib,
|
||||||
|
int streamIndex = 0) const;
|
||||||
|
virtual bool setFieldData(int index, const QVariant &value,
|
||||||
|
FieldAttrib attrib = FieldValue);
|
||||||
|
|
||||||
|
virtual int protocolFrameSize(int streamIndex = 0) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OstProto::Gre data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
39
common/gre.proto
Normal file
39
common/gre.proto
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "protocol.proto";
|
||||||
|
|
||||||
|
package OstProto;
|
||||||
|
|
||||||
|
// GRE Protocol
|
||||||
|
message Gre {
|
||||||
|
optional uint32 flags = 1 [default = 0xa];
|
||||||
|
optional uint32 rsvd0 = 2;
|
||||||
|
optional uint32 version = 3;
|
||||||
|
optional uint32 protocol_type = 4;
|
||||||
|
optional uint32 checksum = 5;
|
||||||
|
optional bool is_override_checksum = 6;
|
||||||
|
optional uint32 rsvd1 = 7;
|
||||||
|
optional uint32 key = 8 [default = 0x2020bad7];
|
||||||
|
optional uint32 sequence_num = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend Protocol {
|
||||||
|
optional Gre gre = 405;
|
||||||
|
}
|
193
common/gre.ui
Normal file
193
common/gre.ui
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Gre</class>
|
||||||
|
<widget class="QWidget" name="Gre">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>264</width>
|
||||||
|
<height>140</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Gre</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Version</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QSpinBox" name="version">
|
||||||
|
<property name="specialValueText">
|
||||||
|
<string>0 (RFC2784)</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>7</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="hasChecksum">
|
||||||
|
<property name="text">
|
||||||
|
<string>Checksum</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="IntEdit" name="checksum">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="specialValueText">
|
||||||
|
<string><auto></string>
|
||||||
|
</property>
|
||||||
|
<property name="prefix">
|
||||||
|
<string>0x</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
<property name="displayIntegerBase">
|
||||||
|
<number>16</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="hasKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Key</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="UIntEdit" name="key">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="hasSequence">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sequence No</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="UIntEdit" name="sequence">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>IntEdit</class>
|
||||||
|
<extends>QSpinBox</extends>
|
||||||
|
<header>intedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>UIntEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>uintedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>hasChecksum</tabstop>
|
||||||
|
<tabstop>checksum</tabstop>
|
||||||
|
<tabstop>hasKey</tabstop>
|
||||||
|
<tabstop>key</tabstop>
|
||||||
|
<tabstop>hasSequence</tabstop>
|
||||||
|
<tabstop>sequence</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>hasKey</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>key</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>32</x>
|
||||||
|
<y>69</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>107</x>
|
||||||
|
<y>71</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>hasSequence</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>sequence</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>75</x>
|
||||||
|
<y>99</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>125</x>
|
||||||
|
<y>97</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>hasChecksum</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>checksum</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>87</x>
|
||||||
|
<y>43</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>109</x>
|
||||||
|
<y>45</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
118
common/greconfig.cpp
Normal file
118
common/greconfig.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "greconfig.h"
|
||||||
|
#include "gre.h"
|
||||||
|
|
||||||
|
GreConfigForm::GreConfigForm(QWidget *parent)
|
||||||
|
: AbstractProtocolConfigForm(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
connect(hasChecksum, SIGNAL(clicked(bool)),
|
||||||
|
this, SLOT(setAutoChecksum(bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
GreConfigForm::~GreConfigForm()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GreConfigForm* GreConfigForm::createInstance()
|
||||||
|
{
|
||||||
|
return new GreConfigForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load widget contents from proto
|
||||||
|
void GreConfigForm::loadWidget(AbstractProtocol *proto)
|
||||||
|
{
|
||||||
|
uint flags = proto->fieldData(GreProtocol::gre_flags,
|
||||||
|
AbstractProtocol::FieldValue)
|
||||||
|
.toUInt();
|
||||||
|
|
||||||
|
version->setValue(
|
||||||
|
proto->fieldData(
|
||||||
|
GreProtocol::gre_version,
|
||||||
|
AbstractProtocol::FieldValue
|
||||||
|
).toUInt());
|
||||||
|
|
||||||
|
hasChecksum->setChecked(flags & GRE_FLAG_CKSUM);
|
||||||
|
checksum->setValue(
|
||||||
|
proto->fieldData(
|
||||||
|
GreProtocol::gre_isOverrideChecksum,
|
||||||
|
AbstractProtocol::FieldValue).toBool() ?
|
||||||
|
proto->fieldData(
|
||||||
|
GreProtocol::gre_checksum,
|
||||||
|
AbstractProtocol::FieldValue).toUInt() : -1);
|
||||||
|
|
||||||
|
hasKey->setChecked(flags & GRE_FLAG_KEY);
|
||||||
|
key->setValue(
|
||||||
|
proto->fieldData(
|
||||||
|
GreProtocol::gre_key,
|
||||||
|
AbstractProtocol::FieldValue
|
||||||
|
).toUInt());
|
||||||
|
|
||||||
|
hasSequence->setChecked(flags & GRE_FLAG_SEQ);
|
||||||
|
sequence->setValue(
|
||||||
|
proto->fieldData(
|
||||||
|
GreProtocol::gre_sequence,
|
||||||
|
AbstractProtocol::FieldValue
|
||||||
|
).toUInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store widget contents into proto
|
||||||
|
void GreConfigForm::storeWidget(AbstractProtocol *proto)
|
||||||
|
{
|
||||||
|
uint flags = 0;
|
||||||
|
|
||||||
|
if (hasChecksum->isChecked())
|
||||||
|
flags |= GRE_FLAG_CKSUM;
|
||||||
|
if (hasKey->isChecked())
|
||||||
|
flags |= GRE_FLAG_KEY;
|
||||||
|
if (hasSequence->isChecked())
|
||||||
|
flags |= GRE_FLAG_SEQ;
|
||||||
|
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_flags,
|
||||||
|
flags);
|
||||||
|
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_version,
|
||||||
|
version->value());
|
||||||
|
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_checksum,
|
||||||
|
checksum->value());
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_isOverrideChecksum,
|
||||||
|
checksum->value() > -1 ? true: false);
|
||||||
|
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_key,
|
||||||
|
key->value());
|
||||||
|
|
||||||
|
proto->setFieldData(
|
||||||
|
GreProtocol::gre_sequence,
|
||||||
|
sequence->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreConfigForm::setAutoChecksum(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
checksum->setValue(-1); // auto
|
||||||
|
}
|
44
common/greconfig.h
Normal file
44
common/greconfig.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010, 2014 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 _GRE_CONFIG_H
|
||||||
|
#define _GRE_CONFIG_H
|
||||||
|
|
||||||
|
#include "abstractprotocolconfig.h"
|
||||||
|
#include "ui_gre.h"
|
||||||
|
|
||||||
|
class GreConfigForm :
|
||||||
|
public AbstractProtocolConfigForm,
|
||||||
|
private Ui::Gre
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
GreConfigForm(QWidget *parent = 0);
|
||||||
|
virtual ~GreConfigForm();
|
||||||
|
|
||||||
|
static GreConfigForm* createInstance();
|
||||||
|
|
||||||
|
virtual void loadWidget(AbstractProtocol *proto);
|
||||||
|
virtual void storeWidget(AbstractProtocol *proto);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void setAutoChecksum(bool enabled);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
69
common/grepdml.cpp
Normal file
69
common/grepdml.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 "grepdml.h"
|
||||||
|
|
||||||
|
#include "gre.pb.h"
|
||||||
|
|
||||||
|
PdmlGreProtocol::PdmlGreProtocol()
|
||||||
|
{
|
||||||
|
ostProtoId_ = OstProto::Protocol::kGreFieldNumber;
|
||||||
|
|
||||||
|
fieldMap_.insert("gre.proto", OstProto::Gre::kProtocolTypeFieldNumber);
|
||||||
|
fieldMap_.insert("gre.checksum", OstProto::Gre::kChecksumFieldNumber);
|
||||||
|
fieldMap_.insert("gre.offset", OstProto::Gre::kRsvd1FieldNumber);
|
||||||
|
fieldMap_.insert("gre.key", OstProto::Gre::kKeyFieldNumber);
|
||||||
|
fieldMap_.insert("gre.sequence_number", OstProto::Gre::kSequenceNumFieldNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
PdmlGreProtocol::~PdmlGreProtocol()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PdmlProtocol* PdmlGreProtocol::createInstance()
|
||||||
|
{
|
||||||
|
return new PdmlGreProtocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PdmlGreProtocol::postProtocolHandler(OstProto::Protocol* pbProto,
|
||||||
|
OstProto::Stream* /*stream*/)
|
||||||
|
{
|
||||||
|
OstProto::Gre *gre = pbProto->MutableExtension(OstProto::gre);
|
||||||
|
|
||||||
|
qDebug("GRE: post");
|
||||||
|
|
||||||
|
gre->set_is_override_checksum(overrideCksum_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PdmlGreProtocol::unknownFieldHandler(QString name,
|
||||||
|
int /*pos*/, int /*size*/, const QXmlStreamAttributes& attributes,
|
||||||
|
OstProto::Protocol* proto, OstProto::Stream* /*stream*/)
|
||||||
|
{
|
||||||
|
if (name == "gre.flags_and_version") {
|
||||||
|
bool isOk;
|
||||||
|
OstProto::Gre *gre = proto->MutableExtension(OstProto::gre);
|
||||||
|
quint16 flagsAndVersion = attributes.value("value")
|
||||||
|
.toUInt(&isOk, kBaseHex);
|
||||||
|
|
||||||
|
gre->set_flags(flagsAndVersion >> 12);
|
||||||
|
gre->set_rsvd0((flagsAndVersion & 0x0FFF) >> 3);
|
||||||
|
gre->set_version(flagsAndVersion & 0x0007);
|
||||||
|
}
|
||||||
|
}
|
45
common/grepdml.h
Normal file
45
common/grepdml.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _GRE_PDML_H
|
||||||
|
#define _GRE_PDML_H
|
||||||
|
|
||||||
|
#include "pdmlprotocol.h"
|
||||||
|
|
||||||
|
class PdmlGreProtocol : public PdmlProtocol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PdmlGreProtocol();
|
||||||
|
|
||||||
|
static PdmlProtocol* createInstance();
|
||||||
|
|
||||||
|
virtual void postProtocolHandler(OstProto::Protocol *pbProto,
|
||||||
|
OstProto::Stream *stream);
|
||||||
|
|
||||||
|
void fieldHandler(QString name, const QXmlStreamAttributes &attributes,
|
||||||
|
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||||
|
virtual void unknownFieldHandler(QString name, int pos, int size,
|
||||||
|
const QXmlStreamAttributes &attributes,
|
||||||
|
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PdmlGreProtocol();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -197,24 +197,11 @@ QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
quint16 cks;
|
cksum = (icmpVersion() == OstProto::Icmp::kIcmp4) ?
|
||||||
quint32 sum = 0;
|
AbstractProtocol::protocolFrameCksum(
|
||||||
|
streamIndex, CksumIcmpIgmp) :
|
||||||
cks = protocolFrameCksum(streamIndex, CksumIp);
|
AbstractProtocol::protocolFrameCksum(
|
||||||
sum += (quint16) ~cks;
|
streamIndex, CksumTcpUdp);
|
||||||
cks = protocolFramePayloadCksum(streamIndex, CksumIp);
|
|
||||||
sum += (quint16) ~cks;
|
|
||||||
if (icmpVersion() == OstProto::Icmp::kIcmp6)
|
|
||||||
{
|
|
||||||
cks = protocolFrameHeaderCksum(streamIndex,
|
|
||||||
CksumIpPseudo);
|
|
||||||
sum += (quint16) ~cks;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(sum>>16)
|
|
||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
||||||
|
|
||||||
cksum = (~sum) & 0xFFFF;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -56,7 +56,7 @@ void PdmlIcmpProtocol::preProtocolHandler(QString name,
|
|||||||
else if (name == "icmpv6")
|
else if (name == "icmpv6")
|
||||||
icmp->set_icmp_version(OstProto::Icmp::kIcmp6);
|
icmp->set_icmp_version(OstProto::Icmp::kIcmp6);
|
||||||
|
|
||||||
icmp->set_is_override_checksum(true);
|
icmp->set_is_override_checksum(overrideCksum_);
|
||||||
|
|
||||||
icmp->set_type(kIcmpInvalidType);
|
icmp->set_type(kIcmpInvalidType);
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
case FieldName:
|
case FieldName:
|
||||||
return QString("Group Address");
|
return QString("Group Address");
|
||||||
case FieldValue:
|
case FieldValue:
|
||||||
|
return grpIp;
|
||||||
case FieldTextValue:
|
case FieldTextValue:
|
||||||
return QHostAddress(grpIp).toString();
|
return QHostAddress(grpIp).toString();
|
||||||
case FieldFrameValue:
|
case FieldFrameValue:
|
||||||
@ -289,8 +290,14 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
}
|
}
|
||||||
case kGroupAddress:
|
case kGroupAddress:
|
||||||
{
|
{
|
||||||
|
quint32 ip = value.toUInt(&isOk);
|
||||||
|
if (isOk) {
|
||||||
|
data.mutable_group_address()->set_v4(ip);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
QHostAddress addr(value.toString());
|
QHostAddress addr(value.toString());
|
||||||
quint32 ip = addr.toIPv4Address();
|
ip = addr.toIPv4Address();
|
||||||
isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol);
|
isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol);
|
||||||
if (isOk)
|
if (isOk)
|
||||||
data.mutable_group_address()->set_v4(ip);
|
data.mutable_group_address()->set_v4(ip);
|
||||||
@ -306,6 +313,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
quint32 ip = QHostAddress(str).toIPv4Address();
|
quint32 ip = QHostAddress(str).toIPv4Address();
|
||||||
data.add_sources()->set_v4(ip);
|
data.add_sources()->set_v4(ip);
|
||||||
}
|
}
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +340,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
QHostAddress(src).toIPv4Address());
|
QHostAddress(src).toIPv4Address());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isOk = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -347,18 +356,5 @@ _exit:
|
|||||||
|
|
||||||
quint16 IgmpProtocol::checksum(int streamIndex) const
|
quint16 IgmpProtocol::checksum(int streamIndex) const
|
||||||
{
|
{
|
||||||
quint16 cks;
|
return AbstractProtocol::protocolFrameCksum(streamIndex, CksumIcmpIgmp);
|
||||||
quint32 sum = 0;
|
|
||||||
|
|
||||||
// TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto
|
|
||||||
cks = protocolFrameCksum(streamIndex, CksumIp);
|
|
||||||
sum += (quint16) ~cks;
|
|
||||||
cks = protocolFramePayloadCksum(streamIndex, CksumIp);
|
|
||||||
sum += (quint16) ~cks;
|
|
||||||
while (sum >> 16)
|
|
||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
||||||
|
|
||||||
cks = (~sum) & 0xFFFF;
|
|
||||||
|
|
||||||
return cks;
|
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/,
|
|||||||
OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp);
|
OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp);
|
||||||
|
|
||||||
igmp->set_is_override_rsvd_code(true);
|
igmp->set_is_override_rsvd_code(true);
|
||||||
igmp->set_is_override_checksum(true);
|
igmp->set_is_override_checksum(overrideCksum_);
|
||||||
igmp->set_is_override_source_count(true);
|
igmp->set_is_override_source_count(true);
|
||||||
igmp->set_is_override_group_record_count(true);
|
igmp->set_is_override_group_record_count(true);
|
||||||
|
|
||||||
|
@ -845,7 +845,7 @@ int Ip4Protocol::protocolFrameVariableCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
|
quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
|
||||||
CksumType cksumType) const
|
CksumType cksumType, CksumFlags cksumFlags) const
|
||||||
{
|
{
|
||||||
switch (cksumType)
|
switch (cksumType)
|
||||||
{
|
{
|
||||||
@ -876,7 +876,8 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
|
return AbstractProtocol::protocolFrameCksum(
|
||||||
|
streamIndex, cksumType, cksumFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ip4Protocol::hasErrors(QStringList *errors) const
|
bool Ip4Protocol::hasErrors(QStringList *errors) const
|
||||||
|
@ -89,7 +89,7 @@ public:
|
|||||||
virtual int protocolFrameVariableCount() const;
|
virtual int protocolFrameVariableCount() const;
|
||||||
|
|
||||||
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
||||||
CksumType cksumType = CksumIp) const;
|
CksumType cksumType = CksumIp, CksumFlags cksumFlags = 0) const;
|
||||||
|
|
||||||
virtual bool hasErrors(QStringList *errors = nullptr) const;
|
virtual bool hasErrors(QStringList *errors = nullptr) const;
|
||||||
private:
|
private:
|
||||||
|
@ -88,6 +88,6 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto,
|
|||||||
ip4->set_is_override_hdrlen(true);
|
ip4->set_is_override_hdrlen(true);
|
||||||
ip4->set_is_override_totlen(true);
|
ip4->set_is_override_totlen(true);
|
||||||
ip4->set_is_override_proto(true);
|
ip4->set_is_override_proto(true);
|
||||||
ip4->set_is_override_cksum(true);
|
ip4->set_is_override_cksum(overrideCksum_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,6 +354,11 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
case FieldName:
|
case FieldName:
|
||||||
return QString("Source");
|
return QString("Source");
|
||||||
case FieldValue:
|
case FieldValue:
|
||||||
|
{
|
||||||
|
QVariant v;
|
||||||
|
v.setValue(src);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
case FieldFrameValue:
|
case FieldFrameValue:
|
||||||
case FieldTextValue:
|
case FieldTextValue:
|
||||||
{
|
{
|
||||||
@ -412,6 +417,11 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
case FieldName:
|
case FieldName:
|
||||||
return QString("Destination");
|
return QString("Destination");
|
||||||
case FieldValue:
|
case FieldValue:
|
||||||
|
{
|
||||||
|
QVariant v;
|
||||||
|
v.setValue(dst);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
case FieldFrameValue:
|
case FieldFrameValue:
|
||||||
case FieldTextValue:
|
case FieldTextValue:
|
||||||
{
|
{
|
||||||
@ -594,6 +604,14 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
|||||||
}
|
}
|
||||||
case ip6_srcAddress:
|
case ip6_srcAddress:
|
||||||
{
|
{
|
||||||
|
if (value.typeName() == QString("UInt128")) {
|
||||||
|
UInt128 addr = value.value<UInt128>();
|
||||||
|
data.set_src_addr_hi(addr.hi64());
|
||||||
|
data.set_src_addr_lo(addr.lo64());
|
||||||
|
isOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||||
quint64 x;
|
quint64 x;
|
||||||
|
|
||||||
@ -616,10 +634,19 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
|||||||
| (quint64(addr[14]) << 8)
|
| (quint64(addr[14]) << 8)
|
||||||
| (quint64(addr[15]) << 0);
|
| (quint64(addr[15]) << 0);
|
||||||
data.set_src_addr_lo(x);
|
data.set_src_addr_lo(x);
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ip6_dstAddress:
|
case ip6_dstAddress:
|
||||||
{
|
{
|
||||||
|
if (value.typeName() == QString("UInt128")) {
|
||||||
|
UInt128 addr = value.value<UInt128>();
|
||||||
|
data.set_dst_addr_hi(addr.hi64());
|
||||||
|
data.set_dst_addr_lo(addr.lo64());
|
||||||
|
isOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||||
quint64 x;
|
quint64 x;
|
||||||
|
|
||||||
@ -642,6 +669,7 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
|||||||
| (quint64(addr[14]) << 8)
|
| (quint64(addr[14]) << 8)
|
||||||
| (quint64(addr[15]) << 0);
|
| (quint64(addr[15]) << 0);
|
||||||
data.set_dst_addr_lo(x);
|
data.set_dst_addr_lo(x);
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +768,7 @@ int Ip6Protocol::protocolFrameVariableCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
|
quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
|
||||||
CksumType cksumType) const
|
CksumType cksumType, CksumFlags cksumFlags) const
|
||||||
{
|
{
|
||||||
if (cksumType == CksumIpPseudo)
|
if (cksumType == CksumIpPseudo)
|
||||||
{
|
{
|
||||||
@ -764,7 +792,8 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
|
|||||||
|
|
||||||
return qFromBigEndian((quint16) ~sum);
|
return qFromBigEndian((quint16) ~sum);
|
||||||
}
|
}
|
||||||
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
|
return AbstractProtocol::protocolFrameCksum(
|
||||||
|
streamIndex, cksumType, cksumFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ip6Protocol::hasErrors(QStringList *errors) const
|
bool Ip6Protocol::hasErrors(QStringList *errors) const
|
||||||
|
@ -103,7 +103,7 @@ public:
|
|||||||
virtual int protocolFrameVariableCount() const;
|
virtual int protocolFrameVariableCount() const;
|
||||||
|
|
||||||
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
||||||
CksumType cksumType = CksumIp) const;
|
CksumType cksumType = CksumIp, CksumFlags cksumFlags = 0) const;
|
||||||
|
|
||||||
virtual bool hasErrors(QStringList *errors = nullptr) const;
|
virtual bool hasErrors(QStringList *errors = nullptr) const;
|
||||||
private:
|
private:
|
||||||
|
@ -20,6 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#ifndef _IP_UTILS_H
|
#ifndef _IP_UTILS_H
|
||||||
#define _IP_UTILS_H
|
#define _IP_UTILS_H
|
||||||
|
|
||||||
|
#include "uint128.h"
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
namespace ipUtils {
|
namespace ipUtils {
|
||||||
enum AddrMode {
|
enum AddrMode {
|
||||||
kFixed = 0,
|
kFixed = 0,
|
||||||
@ -118,5 +122,31 @@ void inline ipAddress(quint64 baseIpHi, quint64 baseIpLo, int prefix,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UInt128 inline ip6StringToUInt128(QString ip6Str)
|
||||||
|
{
|
||||||
|
Q_IPV6ADDR addr = QHostAddress(ip6Str).toIPv6Address();
|
||||||
|
quint64 hi, lo;
|
||||||
|
|
||||||
|
hi = (quint64(addr[0]) << 56)
|
||||||
|
| (quint64(addr[1]) << 48)
|
||||||
|
| (quint64(addr[2]) << 40)
|
||||||
|
| (quint64(addr[3]) << 32)
|
||||||
|
| (quint64(addr[4]) << 24)
|
||||||
|
| (quint64(addr[5]) << 16)
|
||||||
|
| (quint64(addr[6]) << 8)
|
||||||
|
| (quint64(addr[7]) << 0);
|
||||||
|
|
||||||
|
lo = (quint64(addr[ 8]) << 56)
|
||||||
|
| (quint64(addr[ 9]) << 48)
|
||||||
|
| (quint64(addr[10]) << 40)
|
||||||
|
| (quint64(addr[11]) << 32)
|
||||||
|
| (quint64(addr[12]) << 24)
|
||||||
|
| (quint64(addr[13]) << 16)
|
||||||
|
| (quint64(addr[14]) << 8)
|
||||||
|
| (quint64(addr[15]) << 0);
|
||||||
|
|
||||||
|
return UInt128(hi, lo);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ipUtils
|
} // namespace ipUtils
|
||||||
#endif
|
#endif
|
||||||
|
48
common/ipv4addressvalidator.h
Normal file
48
common/ipv4addressvalidator.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _IPV4_ADDRESS_VALIDATOR_H
|
||||||
|
#define _IPV4_ADDRESS_VALIDATOR_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
|
||||||
|
class IPv4AddressValidator : public QRegularExpressionValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IPv4AddressValidator(QObject *parent = 0)
|
||||||
|
: QRegularExpressionValidator(
|
||||||
|
QRegularExpression(
|
||||||
|
"((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)(\\.(?!$)|$)){4}"),
|
||||||
|
parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void fixup(QString &input) const
|
||||||
|
{
|
||||||
|
QStringList bytes = input.split('.', QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
while (bytes.count() < 4)
|
||||||
|
bytes.append("0");
|
||||||
|
|
||||||
|
input = bytes.join('.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -279,14 +279,16 @@ bool MacProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
{
|
{
|
||||||
case mac_dstAddr:
|
case mac_dstAddr:
|
||||||
{
|
{
|
||||||
quint64 mac = value.toULongLong();
|
quint64 mac = value.toULongLong(&isOk);
|
||||||
data.set_dst_mac(mac);
|
if (isOk)
|
||||||
|
data.set_dst_mac(mac);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case mac_srcAddr:
|
case mac_srcAddr:
|
||||||
{
|
{
|
||||||
quint64 mac = value.toULongLong();
|
quint64 mac = value.toULongLong(&isOk);
|
||||||
data.set_src_mac(mac);
|
if (isOk)
|
||||||
|
data.set_src_mac(mac);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
52
common/macaddressvalidator.h
Normal file
52
common/macaddressvalidator.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 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 _MAC_ADDRESS_VALIDATOR_H
|
||||||
|
#define _MAC_ADDRESS_VALIDATOR_H
|
||||||
|
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
|
||||||
|
// Allow : or - as separator
|
||||||
|
class MacAddressValidator : public QRegularExpressionValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MacAddressValidator(QObject *parent = 0)
|
||||||
|
: QRegularExpressionValidator(
|
||||||
|
QRegularExpression(
|
||||||
|
"([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"),
|
||||||
|
parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void fixup(QString &input) const
|
||||||
|
{
|
||||||
|
QStringList bytes = input.split(QRegularExpression("[:-]"),
|
||||||
|
QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
if (!bytes.isEmpty() && bytes.last().size() == 1)
|
||||||
|
bytes.last().prepend("0");
|
||||||
|
|
||||||
|
while (bytes.count() < 6)
|
||||||
|
bytes.append("00");
|
||||||
|
|
||||||
|
input = bytes.join(":");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#define _MAC_EDIT_H
|
#define _MAC_EDIT_H
|
||||||
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
|
||||||
class MacEdit: public QLineEdit
|
class MacEdit: public QLineEdit
|
||||||
{
|
{
|
||||||
|
@ -195,6 +195,7 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
case FieldName:
|
case FieldName:
|
||||||
return QString("Group Address");
|
return QString("Group Address");
|
||||||
case FieldValue:
|
case FieldValue:
|
||||||
|
return QVariant::fromValue(UInt128(grpHi, grpLo));
|
||||||
case FieldTextValue:
|
case FieldTextValue:
|
||||||
case FieldFrameValue:
|
case FieldFrameValue:
|
||||||
{
|
{
|
||||||
@ -401,6 +402,14 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
{
|
{
|
||||||
case kGroupAddress:
|
case kGroupAddress:
|
||||||
{
|
{
|
||||||
|
if (value.typeName() == QString("UInt128")) {
|
||||||
|
UInt128 addr = value.value<UInt128>();
|
||||||
|
data.mutable_group_address()->set_v6_hi(addr.hi64());
|
||||||
|
data.mutable_group_address()->set_v6_lo(addr.lo64());
|
||||||
|
isOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||||
quint64 x;
|
quint64 x;
|
||||||
|
|
||||||
@ -423,6 +432,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
| (quint64(addr[14]) << 8)
|
| (quint64(addr[14]) << 8)
|
||||||
| (quint64(addr[15]) << 0);
|
| (quint64(addr[15]) << 0);
|
||||||
data.mutable_group_address()->set_v6_lo(x);
|
data.mutable_group_address()->set_v6_lo(x);
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +467,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
| (quint64(addr[15]) << 0);
|
| (quint64(addr[15]) << 0);
|
||||||
src->set_v6_lo(x);
|
src->set_v6_lo(x);
|
||||||
}
|
}
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,7 +535,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
src->set_v6_lo(x);
|
src->set_v6_lo(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ void PdmlMldProtocol::preProtocolHandler(QString /*name*/,
|
|||||||
OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld);
|
OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld);
|
||||||
|
|
||||||
mld->set_is_override_rsvd_code(true);
|
mld->set_is_override_rsvd_code(true);
|
||||||
mld->set_is_override_checksum(true);
|
mld->set_is_override_checksum(overrideCksum_);
|
||||||
mld->set_is_override_source_count(true);
|
mld->set_is_override_source_count(true);
|
||||||
mld->set_is_override_group_record_count(true);
|
mld->set_is_override_group_record_count(true);
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ PROTOS += \
|
|||||||
ip4over6.proto \
|
ip4over6.proto \
|
||||||
ip4over4.proto \
|
ip4over4.proto \
|
||||||
ip6over6.proto \
|
ip6over6.proto \
|
||||||
|
gre.proto \
|
||||||
icmp.proto \
|
icmp.proto \
|
||||||
gmp.proto \
|
gmp.proto \
|
||||||
igmp.proto \
|
igmp.proto \
|
||||||
@ -69,6 +70,7 @@ HEADERS += \
|
|||||||
ip6over4.h \
|
ip6over4.h \
|
||||||
ip6over6.h \
|
ip6over6.h \
|
||||||
gmp.h \
|
gmp.h \
|
||||||
|
gre.h \
|
||||||
icmp.h \
|
icmp.h \
|
||||||
igmp.h \
|
igmp.h \
|
||||||
mld.h \
|
mld.h \
|
||||||
@ -103,6 +105,7 @@ SOURCES += \
|
|||||||
ip4.cpp \
|
ip4.cpp \
|
||||||
ip6.cpp \
|
ip6.cpp \
|
||||||
gmp.cpp \
|
gmp.cpp \
|
||||||
|
gre.cpp \
|
||||||
icmp.cpp \
|
icmp.cpp \
|
||||||
igmp.cpp \
|
igmp.cpp \
|
||||||
mld.cpp \
|
mld.cpp \
|
||||||
|
@ -20,6 +20,7 @@ FORMS += \
|
|||||||
ip4.ui \
|
ip4.ui \
|
||||||
ip6.ui \
|
ip6.ui \
|
||||||
gmp.ui \
|
gmp.ui \
|
||||||
|
gre.ui \
|
||||||
icmp.ui \
|
icmp.ui \
|
||||||
tcp.ui \
|
tcp.ui \
|
||||||
udp.ui \
|
udp.ui \
|
||||||
@ -75,6 +76,7 @@ HEADERS += \
|
|||||||
ip6config.h \
|
ip6config.h \
|
||||||
ip4over4config.h \
|
ip4over4config.h \
|
||||||
gmpconfig.h \
|
gmpconfig.h \
|
||||||
|
greconfig.h \
|
||||||
icmpconfig.h \
|
icmpconfig.h \
|
||||||
igmpconfig.h \
|
igmpconfig.h \
|
||||||
mldconfig.h \
|
mldconfig.h \
|
||||||
@ -118,6 +120,7 @@ SOURCES += \
|
|||||||
ip4config.cpp \
|
ip4config.cpp \
|
||||||
ip6config.cpp \
|
ip6config.cpp \
|
||||||
gmpconfig.cpp \
|
gmpconfig.cpp \
|
||||||
|
greconfig.cpp \
|
||||||
icmpconfig.cpp \
|
icmpconfig.cpp \
|
||||||
igmpconfig.cpp \
|
igmpconfig.cpp \
|
||||||
mldconfig.cpp \
|
mldconfig.cpp \
|
||||||
@ -139,6 +142,7 @@ SOURCES += \
|
|||||||
arppdml.cpp \
|
arppdml.cpp \
|
||||||
ip4pdml.cpp \
|
ip4pdml.cpp \
|
||||||
ip6pdml.cpp \
|
ip6pdml.cpp \
|
||||||
|
grepdml.cpp \
|
||||||
icmppdml.cpp \
|
icmppdml.cpp \
|
||||||
icmp6pdml.cpp \
|
icmp6pdml.cpp \
|
||||||
igmppdml.cpp \
|
igmppdml.cpp \
|
||||||
|
@ -232,24 +232,9 @@ int PayloadProtocol::protocolFrameVariableCount() const
|
|||||||
int count = AbstractProtocol::protocolFrameVariableCount();
|
int count = AbstractProtocol::protocolFrameVariableCount();
|
||||||
|
|
||||||
if (data.pattern_mode() == OstProto::Payload::e_dp_random)
|
if (data.pattern_mode() == OstProto::Payload::e_dp_random)
|
||||||
{
|
return mpStream->frameCount();
|
||||||
switch(mpStream->sendUnit())
|
|
||||||
{
|
|
||||||
case OstProto::StreamControl::e_su_packets:
|
|
||||||
return mpStream->numPackets();
|
|
||||||
|
|
||||||
case OstProto::StreamControl::e_su_bursts:
|
count = AbstractProtocol::lcm(count, mpStream->frameSizeVariableCount());
|
||||||
return int(mpStream->numBursts()
|
|
||||||
* mpStream->burstSize()
|
|
||||||
* mpStream->burstRate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mpStream->lenMode() != StreamBase::e_fl_fixed)
|
|
||||||
{
|
|
||||||
count = AbstractProtocol::lcm(count,
|
|
||||||
mpStream->frameLenMax() - mpStream->frameLenMin() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
const quint32 kPcapFileMagic = 0xa1b2c3d4;
|
const quint32 kPcapFileMagic = 0xa1b2c3d4;
|
||||||
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
|
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
|
||||||
|
const quint32 kNanoSecondPcapFileMagic = 0xa1b23c4d;
|
||||||
|
const quint32 kNanoSecondPcapFileMagicSwapped = 0x4d3cb2a1;
|
||||||
const quint16 kPcapFileVersionMajor = 2;
|
const quint16 kPcapFileVersionMajor = 2;
|
||||||
const quint16 kPcapFileVersionMinor = 4;
|
const quint16 kPcapFileVersionMinor = 4;
|
||||||
const quint32 kMaxSnapLen = 65535;
|
const quint32 kMaxSnapLen = 65535;
|
||||||
@ -44,9 +46,16 @@ PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
|||||||
: QDialog(NULL)
|
: QDialog(NULL)
|
||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
options_ = options;
|
options_ = options;
|
||||||
|
|
||||||
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
||||||
|
// XXX: By default this key is absent - so that pcap import tests
|
||||||
|
// evaluate to false and hence show minimal diffs.
|
||||||
|
// However, for the GUI user, this should be enabled by default.
|
||||||
|
recalculateCksums->setChecked(
|
||||||
|
options_->value("RecalculateCksums", QVariant(true))
|
||||||
|
.toBool());
|
||||||
doDiff->setChecked(options_->value("DoDiff").toBool());
|
doDiff->setChecked(options_->value("DoDiff").toBool());
|
||||||
|
|
||||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
@ -59,6 +68,7 @@ PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
|||||||
void PcapImportOptionsDialog::accept()
|
void PcapImportOptionsDialog::accept()
|
||||||
{
|
{
|
||||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||||
|
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||||
options_->insert("DoDiff", doDiff->isChecked());
|
options_->insert("DoDiff", doDiff->isChecked());
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
@ -68,13 +78,10 @@ PcapFileFormat::PcapFileFormat()
|
|||||||
{
|
{
|
||||||
importOptions_.insert("ViaPdml", true);
|
importOptions_.insert("ViaPdml", true);
|
||||||
importOptions_.insert("DoDiff", true);
|
importOptions_.insert("DoDiff", true);
|
||||||
|
|
||||||
importDialog_ = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PcapFileFormat::~PcapFileFormat()
|
PcapFileFormat::~PcapFileFormat()
|
||||||
{
|
{
|
||||||
delete importDialog_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PcapFileFormat::open(const QString fileName,
|
bool PcapFileFormat::open(const QString fileName,
|
||||||
@ -85,11 +92,12 @@ bool PcapFileFormat::open(const QString fileName,
|
|||||||
QTemporaryFile file2;
|
QTemporaryFile file2;
|
||||||
quint32 magic;
|
quint32 magic;
|
||||||
uchar gzipMagic[2];
|
uchar gzipMagic[2];
|
||||||
|
bool nsecResolution = false;
|
||||||
int len;
|
int len;
|
||||||
PcapFileHeader fileHdr;
|
PcapFileHeader fileHdr;
|
||||||
PcapPacketHeader pktHdr;
|
PcapPacketHeader pktHdr;
|
||||||
OstProto::Stream *prevStream = NULL;
|
OstProto::Stream *prevStream = NULL;
|
||||||
uint lastUsec = 0;
|
quint64 lastXsec = 0;
|
||||||
int pktCount;
|
int pktCount;
|
||||||
qint64 byteCount = 0;
|
qint64 byteCount = 0;
|
||||||
qint64 byteTotal;
|
qint64 byteTotal;
|
||||||
@ -159,15 +167,22 @@ _retry:
|
|||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
else if (magic == kPcapFileMagicSwapped)
|
else if (magic == kNanoSecondPcapFileMagic)
|
||||||
|
{
|
||||||
|
nsecResolution = true;
|
||||||
|
}
|
||||||
|
else if ((magic == kPcapFileMagicSwapped)
|
||||||
|
|| (magic == kNanoSecondPcapFileMagicSwapped))
|
||||||
{
|
{
|
||||||
// Toggle Byte order
|
// Toggle Byte order
|
||||||
if (fd_.byteOrder() == QDataStream::BigEndian)
|
if (fd_.byteOrder() == QDataStream::BigEndian)
|
||||||
fd_.setByteOrder(QDataStream::LittleEndian);
|
fd_.setByteOrder(QDataStream::LittleEndian);
|
||||||
else
|
else
|
||||||
fd_.setByteOrder(QDataStream::BigEndian);
|
fd_.setByteOrder(QDataStream::BigEndian);
|
||||||
|
|
||||||
|
nsecResolution = (magic == kNanoSecondPcapFileMagicSwapped);
|
||||||
}
|
}
|
||||||
else // Not a pcap file
|
else // Not a pcap file (could be pcapng or something else)
|
||||||
{
|
{
|
||||||
if (tryConvert)
|
if (tryConvert)
|
||||||
{
|
{
|
||||||
@ -219,12 +234,15 @@ _retry:
|
|||||||
|
|
||||||
pktBuf.resize(fileHdr.snapLen);
|
pktBuf.resize(fileHdr.snapLen);
|
||||||
|
|
||||||
|
// XXX: PDML also needs the PCAP file to cross check packet bytes
|
||||||
|
// with the PDML data, so we can't do PDML conversion any earlier
|
||||||
|
// than this
|
||||||
qDebug("pdml check");
|
qDebug("pdml check");
|
||||||
if (importOptions_.value("ViaPdml").toBool())
|
if (importOptions_.value("ViaPdml").toBool())
|
||||||
{
|
{
|
||||||
QProcess tshark;
|
QProcess tshark;
|
||||||
QTemporaryFile pdmlFile;
|
QTemporaryFile pdmlFile;
|
||||||
PdmlReader reader(&streams);
|
PdmlReader reader(&streams, importOptions_);
|
||||||
|
|
||||||
if (!pdmlFile.open())
|
if (!pdmlFile.open())
|
||||||
{
|
{
|
||||||
@ -258,6 +276,8 @@ _retry:
|
|||||||
|
|
||||||
emit status("Reading PDML packets...");
|
emit status("Reading PDML packets...");
|
||||||
emit target(100); // in percentage
|
emit target(100); // in percentage
|
||||||
|
|
||||||
|
// pdml reader needs pcap, so pass self
|
||||||
isOk = reader.read(&pdmlFile, this, &stop_);
|
isOk = reader.read(&pdmlFile, this, &stop_);
|
||||||
|
|
||||||
if (stop_)
|
if (stop_)
|
||||||
@ -453,6 +473,7 @@ _retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
_non_pdml:
|
_non_pdml:
|
||||||
|
qDebug("pcap resolution: %s", nsecResolution ? "nsec" : "usec");
|
||||||
emit status("Reading Packets...");
|
emit status("Reading Packets...");
|
||||||
emit target(100); // in percentage
|
emit target(100); // in percentage
|
||||||
pktCount = 1;
|
pktCount = 1;
|
||||||
@ -480,20 +501,22 @@ _non_pdml:
|
|||||||
stream->mutable_control()->set_num_packets(1);
|
stream->mutable_control()->set_num_packets(1);
|
||||||
|
|
||||||
// setup packet rate to the timing in pcap (as close as possible)
|
// setup packet rate to the timing in pcap (as close as possible)
|
||||||
const double kUsecsInSec = 1e6;
|
// use quint64 rather than double to store micro/nano second as
|
||||||
uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec);
|
// it has a larger range (~580 years) and therefore better accuracy
|
||||||
uint delta = usec - lastUsec;
|
const quint64 kXsecsInSec = nsecResolution ? 1e9 : 1e6;
|
||||||
|
quint64 xsec = (pktHdr.tsSec*kXsecsInSec + pktHdr.tsUsec);
|
||||||
|
quint64 delta = xsec - lastXsec;
|
||||||
|
qDebug("pktCount = %d, delta = %llu", pktCount, delta);
|
||||||
|
|
||||||
if ((pktCount != 1) && delta)
|
if ((pktCount != 1) && delta)
|
||||||
stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta);
|
stream->mutable_control()->set_packets_per_sec(double(kXsecsInSec)/delta);
|
||||||
|
|
||||||
if (prevStream)
|
if (prevStream)
|
||||||
prevStream->mutable_control()->CopyFrom(stream->control());
|
prevStream->mutable_control()->CopyFrom(stream->control());
|
||||||
|
|
||||||
lastUsec = usec;
|
lastXsec = xsec;
|
||||||
prevStream = stream;
|
prevStream = stream;
|
||||||
pktCount++;
|
pktCount++;
|
||||||
qDebug("pktCount = %d", pktCount);
|
|
||||||
byteCount += pktHdr.inclLen + sizeof(pktHdr);
|
byteCount += pktHdr.inclLen + sizeof(pktHdr);
|
||||||
emit progress(int(byteCount*100/byteTotal)); // in percentage
|
emit progress(int(byteCount*100/byteTotal)); // in percentage
|
||||||
if (stop_)
|
if (stop_)
|
||||||
@ -569,7 +592,7 @@ bool PcapFileFormat::convertToStandardPcap(
|
|||||||
tshark.start(OstProtoLib::tsharkPath(),
|
tshark.start(OstProtoLib::tsharkPath(),
|
||||||
QStringList()
|
QStringList()
|
||||||
<< QString("-r%1").arg(fileName)
|
<< QString("-r%1").arg(fileName)
|
||||||
<< "-Fpcap"
|
<< "-Fnsecpcap"
|
||||||
<< QString("-w%1").arg(outputFileName));
|
<< QString("-w%1").arg(outputFileName));
|
||||||
if (!tshark.waitForStarted(-1))
|
if (!tshark.waitForStarted(-1))
|
||||||
{
|
{
|
||||||
@ -632,7 +655,7 @@ bool PcapFileFormat::save(const OstProto::StreamConfigList streams,
|
|||||||
|
|
||||||
fd_.setDevice(&file);
|
fd_.setDevice(&file);
|
||||||
|
|
||||||
fileHdr.magicNumber = kPcapFileMagic;
|
fileHdr.magicNumber = kNanoSecondPcapFileMagic;
|
||||||
fileHdr.versionMajor = kPcapFileVersionMajor;
|
fileHdr.versionMajor = kPcapFileVersionMajor;
|
||||||
fileHdr.versionMinor = kPcapFileVersionMinor;
|
fileHdr.versionMinor = kPcapFileVersionMinor;
|
||||||
fileHdr.thisZone = 0;
|
fileHdr.thisZone = 0;
|
||||||
@ -680,11 +703,16 @@ bool PcapFileFormat::save(const OstProto::StreamConfigList streams,
|
|||||||
fd_.writeRawData(pktBuf.data(), pktHdr.inclLen);
|
fd_.writeRawData(pktBuf.data(), pktHdr.inclLen);
|
||||||
|
|
||||||
if (s.packetRate())
|
if (s.packetRate())
|
||||||
pktHdr.tsUsec += quint32(1e6/s.packetRate());
|
{
|
||||||
if (pktHdr.tsUsec >= 1000000)
|
quint64 delta = quint64(1e9/s.packetRate());
|
||||||
|
pktHdr.tsSec += delta/quint32(1e9);
|
||||||
|
pktHdr.tsUsec += delta % quint32(1e9);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pktHdr.tsUsec >= quint32(1e9))
|
||||||
{
|
{
|
||||||
pktHdr.tsSec++;
|
pktHdr.tsSec++;
|
||||||
pktHdr.tsUsec -= 1000000;
|
pktHdr.tsUsec -= quint32(1e9);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit progress(i);
|
emit progress(i);
|
||||||
@ -705,10 +733,7 @@ _exit:
|
|||||||
|
|
||||||
QDialog* PcapFileFormat::openOptionsDialog()
|
QDialog* PcapFileFormat::openOptionsDialog()
|
||||||
{
|
{
|
||||||
if (!importDialog_)
|
return new PcapImportOptionsDialog(&importOptions_);
|
||||||
importDialog_ = new PcapImportOptionsDialog(&importOptions_);
|
|
||||||
|
|
||||||
return importDialog_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
||||||
|
@ -81,7 +81,6 @@ private:
|
|||||||
|
|
||||||
QDataStream fd_;
|
QDataStream fd_;
|
||||||
QVariantMap importOptions_;
|
QVariantMap importOptions_;
|
||||||
PcapImportOptionsDialog *importDialog_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PcapFileFormat pcapFileFormat;
|
extern PcapFileFormat pcapFileFormat;
|
||||||
|
@ -1,62 +1,69 @@
|
|||||||
<ui version="4.0" >
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
<class>PcapFileImport</class>
|
<class>PcapFileImport</class>
|
||||||
<widget class="QDialog" name="PcapFileImport" >
|
<widget class="QDialog" name="PcapFileImport">
|
||||||
<property name="geometry" >
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>326</width>
|
<width>326</width>
|
||||||
<height>93</height>
|
<height>132</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle">
|
||||||
<string>PCAP import options</string>
|
<string>PCAP import options</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item>
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="viaPdml" >
|
<widget class="QCheckBox" name="viaPdml">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Intelligent Import (via PDML)</string>
|
<string>Intelligent Import (via PDML)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="0" rowspan="2">
|
||||||
<layout class="QHBoxLayout" >
|
<widget class="QWidget" name="indentSpacing" native="true">
|
||||||
<item>
|
<property name="sizePolicy">
|
||||||
<widget class="QWidget" native="1" name="widget" >
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<property name="sizePolicy" >
|
<horstretch>0</horstretch>
|
||||||
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
|
<verstretch>0</verstretch>
|
||||||
<horstretch>0</horstretch>
|
</sizepolicy>
|
||||||
<verstretch>0</verstretch>
|
</property>
|
||||||
</sizepolicy>
|
<property name="minimumSize">
|
||||||
</property>
|
<size>
|
||||||
<property name="minimumSize" >
|
<width>16</width>
|
||||||
<size>
|
<height>16</height>
|
||||||
<width>16</width>
|
</size>
|
||||||
<height>16</height>
|
</property>
|
||||||
</size>
|
</widget>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="doDiff" >
|
|
||||||
<property name="enabled" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string>Do a diff after import</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="1">
|
||||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
<widget class="QCheckBox" name="recalculateCksums">
|
||||||
<property name="orientation" >
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Recalculate Checksums</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="doDiff">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Do a diff after import</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons" >
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -70,11 +77,11 @@
|
|||||||
<receiver>PcapFileImport</receiver>
|
<receiver>PcapFileImport</receiver>
|
||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>249</x>
|
<x>249</x>
|
||||||
<y>81</y>
|
<y>81</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
<y>90</y>
|
<y>90</y>
|
||||||
</hint>
|
</hint>
|
||||||
@ -86,11 +93,11 @@
|
|||||||
<receiver>PcapFileImport</receiver>
|
<receiver>PcapFileImport</receiver>
|
||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>249</x>
|
<x>249</x>
|
||||||
<y>81</y>
|
<y>81</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>258</x>
|
<x>258</x>
|
||||||
<y>90</y>
|
<y>90</y>
|
||||||
</hint>
|
</hint>
|
||||||
@ -102,13 +109,13 @@
|
|||||||
<receiver>doDiff</receiver>
|
<receiver>doDiff</receiver>
|
||||||
<slot>setEnabled(bool)</slot>
|
<slot>setEnabled(bool)</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>15</x>
|
<x>15</x>
|
||||||
<y>16</y>
|
<y>16</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>37</x>
|
<x>68</x>
|
||||||
<y>42</y>
|
<y>71</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
@ -118,13 +125,45 @@
|
|||||||
<receiver>doDiff</receiver>
|
<receiver>doDiff</receiver>
|
||||||
<slot>setChecked(bool)</slot>
|
<slot>setChecked(bool)</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>151</x>
|
<x>151</x>
|
||||||
<y>14</y>
|
<y>14</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>150</x>
|
<x>181</x>
|
||||||
<y>34</y>
|
<y>71</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>viaPdml</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>recalculateCksums</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>29</x>
|
||||||
|
<y>17</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>38</x>
|
||||||
|
<y>39</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>viaPdml</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>recalculateCksums</receiver>
|
||||||
|
<slot>setChecked(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>67</x>
|
||||||
|
<y>18</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>66</x>
|
||||||
|
<y>33</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
|
@ -102,6 +102,11 @@ int PdmlProtocol::fieldId(QString name) const
|
|||||||
return fieldMap_.value(name);
|
return fieldMap_.value(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PdmlProtocol::setRecalculateCksum(bool recalculate)
|
||||||
|
{
|
||||||
|
overrideCksum_ = !recalculate;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This method is called by PdmlReader before any fields within the protocol
|
This method is called by PdmlReader before any fields within the protocol
|
||||||
are processed. All attributes associated with the 'proto' tag in the PDML
|
are processed. All attributes associated with the 'proto' tag in the PDML
|
||||||
|
@ -40,6 +40,8 @@ public:
|
|||||||
bool hasField(QString name) const;
|
bool hasField(QString name) const;
|
||||||
int fieldId(QString name) const;
|
int fieldId(QString name) const;
|
||||||
|
|
||||||
|
void setRecalculateCksum(bool recalculate);
|
||||||
|
|
||||||
virtual void preProtocolHandler(QString name,
|
virtual void preProtocolHandler(QString name,
|
||||||
const QXmlStreamAttributes &attributes, int expectedPos,
|
const QXmlStreamAttributes &attributes, int expectedPos,
|
||||||
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||||
@ -63,6 +65,8 @@ protected:
|
|||||||
int ostProtoId_;
|
int ostProtoId_;
|
||||||
//!< Map of PDML field names to protobuf field numbers for 'known' fields
|
//!< Map of PDML field names to protobuf field numbers for 'known' fields
|
||||||
QMap<QString, int> fieldMap_;
|
QMap<QString, int> fieldMap_;
|
||||||
|
|
||||||
|
bool overrideCksum_{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "arppdml.h"
|
#include "arppdml.h"
|
||||||
#include "eth2pdml.h"
|
#include "eth2pdml.h"
|
||||||
|
#include "grepdml.h"
|
||||||
#include "llcpdml.h"
|
#include "llcpdml.h"
|
||||||
#include "icmppdml.h"
|
#include "icmppdml.h"
|
||||||
#include "icmp6pdml.h"
|
#include "icmp6pdml.h"
|
||||||
@ -42,12 +43,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "udppdml.h"
|
#include "udppdml.h"
|
||||||
#include "vlanpdml.h"
|
#include "vlanpdml.h"
|
||||||
|
|
||||||
PdmlReader::PdmlReader(OstProto::StreamConfigList *streams)
|
PdmlReader::PdmlReader(OstProto::StreamConfigList *streams,
|
||||||
|
const QVariantMap &options)
|
||||||
{
|
{
|
||||||
//gPdmlReader = this;
|
//gPdmlReader = this;
|
||||||
pcap_ = NULL;
|
pcap_ = NULL;
|
||||||
streams_ = streams;
|
streams_ = streams;
|
||||||
|
|
||||||
|
recalculateCksums_ = options.value("RecalculateCksums").toBool();
|
||||||
|
|
||||||
currentStream_ = NULL;
|
currentStream_ = NULL;
|
||||||
prevStream_ = NULL;
|
prevStream_ = NULL;
|
||||||
|
|
||||||
@ -59,6 +63,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams)
|
|||||||
|
|
||||||
factory_.insert("arp", PdmlArpProtocol::createInstance);
|
factory_.insert("arp", PdmlArpProtocol::createInstance);
|
||||||
factory_.insert("eth", PdmlEthProtocol::createInstance);
|
factory_.insert("eth", PdmlEthProtocol::createInstance);
|
||||||
|
factory_.insert("gre", PdmlGreProtocol::createInstance);
|
||||||
factory_.insert("http", PdmlTextProtocol::createInstance);
|
factory_.insert("http", PdmlTextProtocol::createInstance);
|
||||||
factory_.insert("icmp", PdmlIcmpProtocol::createInstance);
|
factory_.insert("icmp", PdmlIcmpProtocol::createInstance);
|
||||||
factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance);
|
factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance);
|
||||||
@ -353,6 +358,8 @@ void PdmlReader::readProto()
|
|||||||
|
|
||||||
pdmlProto = appendPdmlProto(protoName, &pbProto);
|
pdmlProto = appendPdmlProto(protoName, &pbProto);
|
||||||
|
|
||||||
|
pdmlProto->setRecalculateCksum(recalculateCksums_);
|
||||||
|
|
||||||
qDebug("%s: preProtocolHandler(expPos = %d)",
|
qDebug("%s: preProtocolHandler(expPos = %d)",
|
||||||
qPrintable(protoName), expPos_);
|
qPrintable(protoName), expPos_);
|
||||||
pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto,
|
pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto,
|
||||||
|
@ -24,13 +24,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
class PcapFileFormat;
|
class PcapFileFormat;
|
||||||
class PdmlReader : public QObject, public QXmlStreamReader
|
class PdmlReader : public QObject, public QXmlStreamReader
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
PdmlReader(OstProto::StreamConfigList *streams);
|
PdmlReader(OstProto::StreamConfigList *streams,
|
||||||
|
const QVariantMap &options = QVariantMap());
|
||||||
~PdmlReader();
|
~PdmlReader();
|
||||||
|
|
||||||
bool read(QIODevice *device, PcapFileFormat *pcap = NULL,
|
bool read(QIODevice *device, PcapFileFormat *pcap = NULL,
|
||||||
@ -64,6 +66,8 @@ private:
|
|||||||
PcapFileFormat *pcap_;
|
PcapFileFormat *pcap_;
|
||||||
QByteArray pktBuf_;
|
QByteArray pktBuf_;
|
||||||
|
|
||||||
|
bool recalculateCksums_{false};
|
||||||
|
|
||||||
bool isMldSupport_;
|
bool isMldSupport_;
|
||||||
int packetCount_;
|
int packetCount_;
|
||||||
int expPos_;
|
int expPos_;
|
||||||
|
@ -45,6 +45,7 @@ message StreamCore {
|
|||||||
e_fl_inc = 1;
|
e_fl_inc = 1;
|
||||||
e_fl_dec = 2;
|
e_fl_dec = 2;
|
||||||
e_fl_random = 3;
|
e_fl_random = 3;
|
||||||
|
e_fl_imix = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basics
|
// Basics
|
||||||
@ -156,6 +157,7 @@ message Protocol {
|
|||||||
kIcmpFieldNumber = 402;
|
kIcmpFieldNumber = 402;
|
||||||
kIgmpFieldNumber = 403;
|
kIgmpFieldNumber = 403;
|
||||||
kMldFieldNumber = 404;
|
kMldFieldNumber = 404;
|
||||||
|
kGreFieldNumber = 405;
|
||||||
|
|
||||||
kTextProtocolFieldNumber = 500;
|
kTextProtocolFieldNumber = 500;
|
||||||
}
|
}
|
||||||
@ -211,6 +213,9 @@ message Port {
|
|||||||
optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit];
|
optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit];
|
||||||
optional string user_name = 8;
|
optional string user_name = 8;
|
||||||
optional bool is_tracking_stream_stats = 9;
|
optional bool is_tracking_stream_stats = 9;
|
||||||
|
|
||||||
|
optional double speed = 10; // in Mbps
|
||||||
|
optional uint32 mtu = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PortConfigList {
|
message PortConfigList {
|
||||||
@ -285,6 +290,8 @@ message StreamStats {
|
|||||||
required PortId port_id = 1;
|
required PortId port_id = 1;
|
||||||
required StreamGuid stream_guid = 2;
|
required StreamGuid stream_guid = 2;
|
||||||
|
|
||||||
|
optional double tx_duration = 3; // in seconds
|
||||||
|
|
||||||
optional uint64 rx_pkts = 11;
|
optional uint64 rx_pkts = 11;
|
||||||
optional uint64 rx_bytes = 12;
|
optional uint64 rx_bytes = 12;
|
||||||
optional uint64 tx_pkts = 13;
|
optional uint64 tx_pkts = 13;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user