Feature: Variable Fields - any field of any protocol can now be varied by specifying it as a generic 8/16/32 bit field at a specified offset (relative to the protocol frame value) with a certain mask and a (initial) value alongwith count and step; this is working code - but incomplete. Bunch of FIXME/TODOs and testing pending

This commit is contained in:
Srivats P. 2015-05-26 21:43:37 +05:30
parent 77fe49bf10
commit df24cf8b15
14 changed files with 1417 additions and 52 deletions

View File

@ -52,7 +52,8 @@ HEADERS += \
streamconfigdialog.h \
streamlistdelegate.h \
streammodel.h \
updater.h
updater.h \
variablefieldswidget.h
FORMS += \
about.ui \
@ -62,7 +63,8 @@ FORMS += \
portstatswindow.ui \
portswindow.ui \
preferences.ui \
streamconfigdialog.ui
streamconfigdialog.ui \
variablefieldswidget.ui
SOURCES += \
dumpview.cpp \
@ -84,7 +86,8 @@ SOURCES += \
streamconfigdialog.cpp \
streamlistdelegate.cpp \
streammodel.cpp \
updater.cpp
updater.cpp \
variablefieldswidget.cpp
QMAKE_DISTCLEAN += object_script.*

View File

@ -151,6 +151,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&,
const QModelIndex&)));
variableFieldsWidget->setStream(mpStream);
LoadCurrentStream();
mpPacketModel = new PacketModel(this);
tvPacketTree->setModel(mpPacketModel);
@ -644,15 +646,26 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index)
break;
}
// Stream Control
// Variable Fields
case 2:
{
StoreCurrentStream();
// Stream protocols may have changed - clear and reload
variableFieldsWidget->clear();
variableFieldsWidget->load();
break;
}
// Stream Control
case 3:
{
StoreCurrentStream();
break;
}
// Packet View
case 3:
case 4:
{
StoreCurrentStream();
mpPacketModel->setSelectedProtocols(*_iter);
@ -964,6 +977,11 @@ void StreamConfigDialog::LoadCurrentStream()
loadProtocolWidgets();
}
// Variable Fields
{
variableFieldsWidget->load();
}
// Stream Control
{
switch (mpStream->sendUnit())
@ -1039,6 +1057,11 @@ void StreamConfigDialog::StoreCurrentStream()
storeProtocolWidgets();
}
// Variable Fields
{
variableFieldsWidget->store();
}
// Stream Control
{
if (rbSendPackets->isChecked())

View File

@ -767,6 +767,16 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
</item>
</layout>
</widget>
<widget class="QWidget" name="tab" >
<attribute name="title" >
<string>Variable Fields</string>
</attribute>
<layout class="QHBoxLayout" >
<item>
<widget class="VariableFieldsWidget" native="1" name="variableFieldsWidget" />
</item>
</layout>
</widget>
<widget class="QWidget" name="streamControlTab" >
<attribute name="title" >
<string>Stream Control</string>
@ -1209,6 +1219,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
<header>dumpview.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>VariableFieldsWidget</class>
<extends>QWidget</extends>
<header>variablefieldswidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>twTopLevel</tabstop>

View File

@ -0,0 +1,431 @@
/*
Copyright (C) 2015 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 "variablefieldswidget.h"
#include "abstractprotocol.h"
#include "protocollistiterator.h"
#include "stream.h"
#include <QListWidgetItem>
#include <QMetaType>
#include <QStringList>
Q_DECLARE_METATYPE(AbstractProtocol*);
Q_DECLARE_METATYPE(OstProto::VariableField);
QStringList typeNames = QStringList()
<< "Counter8"
<< "Counter16"
<< "Counter32";
QStringList modeNames = QStringList()
<< "Increment"
<< "Decrement"
<< "Random";
#define uintToHexStr(num, bytes) \
QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')).toUpper()
#define hexStrToUInt(str) \
str.toUInt(NULL, BASE_HEX)
/*
* NOTES:
* 1. We use a QSpinBox for all numeric values except for 'value' because
* QSpinBox value is of type 'int' - we would like the ability to store
* quint32
* 2. This widget will keep the stream always updated - every editing change
* of a attribute is immediately updated in the stream; the consequence
* of this design is that an explicit 'store' of widget contents to the
* stream is no longer required - we still define a store() method in
* case we need to change the design later
*/
VariableFieldsWidget::VariableFieldsWidget(QWidget *parent)
: QWidget(parent)
{
stream_ = NULL;
isProgLoad_ = false;
lastSelectedProtocolIndex_ = 0;
lastSelectedVariableFieldIndex_ = 0;
setupUi(this);
attribGroup->setHidden(true);
type->addItems(typeNames);
mode->addItems(modeNames);
valueRange_ = new QIntValidator(this);
// FIXME: we can't use QIntValidator - since we want value to be able
// to enter a quint32
//value->setValidator(valueRange_);
connect(type, SIGNAL(currentIndexChanged(int)),
SLOT(updateCurrentVariableField()));
connect(offset, SIGNAL(valueChanged(int)),
SLOT(updateCurrentVariableField()));
connect(bitmask, SIGNAL(textChanged(QString)),
SLOT(updateCurrentVariableField()));
connect(mode, SIGNAL(currentIndexChanged(int)),
SLOT(updateCurrentVariableField()));
connect(value, SIGNAL(textChanged(QString)),
SLOT(updateCurrentVariableField()));
connect(count, SIGNAL(valueChanged(int)),
SLOT(updateCurrentVariableField()));
connect(step, SIGNAL(valueChanged(int)),
SLOT(updateCurrentVariableField()));
}
void VariableFieldsWidget::setStream(Stream *stream)
{
stream_ = stream;
}
void VariableFieldsWidget::load()
{
Q_ASSERT(stream_);
Q_ASSERT(protocolList->count() == 0);
Q_ASSERT(variableFieldList->count() == 0);
ProtocolListIterator *iter = stream_->createProtocolListIterator();
while (iter->hasNext()) {
AbstractProtocol *proto = iter->next();
QListWidgetItem *protoItem = new QListWidgetItem;
protoItem->setData(Qt::UserRole, QVariant::fromValue(proto));
protoItem->setText(proto->shortName());
protocolList->addItem(protoItem);
}
delete iter;
if (lastSelectedProtocolIndex_ < protocolList->count())
protocolList->setCurrentRow(lastSelectedProtocolIndex_);
// XXX: protocolList->setCurrentRow() above will emit currentItemChanged
// which will load variableFieldsList - no need to load it explicitly
if (lastSelectedVariableFieldIndex_ < variableFieldList->count())
variableFieldList->setCurrentRow(lastSelectedVariableFieldIndex_);
}
void VariableFieldsWidget::store()
{
/* Do Nothing - see Notes at the top of the file */
}
void VariableFieldsWidget::clear()
{
protocolList->clear();
variableFieldList->clear();
}
void VariableFieldsWidget::on_protocolList_currentItemChanged(
QListWidgetItem *current,
QListWidgetItem *previous)
{
AbstractProtocol *proto;
qDebug("%s: curr = %p, prev = %p", __FUNCTION__, current, previous);
if (current == NULL)
goto _exit;
proto = current->data(Qt::UserRole).value<AbstractProtocol*>();
loadProtocolFields(proto);
variableFieldList->clear();
for (int i = 0; i < proto->variableFieldCount(); i++) {
OstProto::VariableField vf = proto->variableField(i);
QListWidgetItem *vfItem = new QListWidgetItem;
setVariableFieldItem(vfItem, proto, vf);
variableFieldList->addItem(vfItem);
}
lastSelectedProtocolIndex_ = protocolList->currentRow();
_exit:
addButton->setEnabled(current != NULL);
}
void VariableFieldsWidget::on_variableFieldList_currentItemChanged(
QListWidgetItem *current,
QListWidgetItem *previous)
{
AbstractProtocol *proto;
OstProto::VariableField vf;
QListWidgetItem *protoItem = protocolList->currentItem();
qDebug("%s: curr = %p, prev = %p, proto = %p",
__FUNCTION__, current, previous, protoItem);
if (current == NULL)
goto _exit;
Q_ASSERT(protoItem);
proto = protoItem->data(Qt::UserRole).value<AbstractProtocol*>();
vf = current->data(Qt::UserRole).value<OstProto::VariableField>();
isProgLoad_ = true;
field->setCurrentIndex(fieldIndex(vf));
type->setCurrentIndex(vf.type());
offset->setValue(vf.offset());
bitmask->setText(uintToHexStr(vf.mask(), typeSize(vf.type())));
value->setText(QString().setNum(vf.value()));
mode->setCurrentIndex(vf.mode());
count->setValue(vf.count());
step->setValue(vf.step());
isProgLoad_ = false;
lastSelectedVariableFieldIndex_ = variableFieldList->currentRow();
_exit:
attribGroup->setHidden(current == NULL);
deleteButton->setEnabled(current != NULL);
}
void VariableFieldsWidget::on_addButton_clicked()
{
QListWidgetItem *protoItem = protocolList->currentItem();
if (!protoItem)
return;
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
.value<AbstractProtocol*>();
OstProto::VariableField vf;
QListWidgetItem *vfItem = new QListWidgetItem;
proto->appendVariableField(vf);
setVariableFieldItem(vfItem, proto, vf);
variableFieldList->addItem(vfItem);
}
void VariableFieldsWidget::on_deleteButton_clicked()
{
QListWidgetItem *protoItem = protocolList->currentItem();
int vfIdx = variableFieldList->currentRow();
if (!protoItem || (vfIdx < 0))
return;
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
.value<AbstractProtocol*>();
proto->removeVariableField(vfIdx);
delete variableFieldList->takeItem(vfIdx);
}
void VariableFieldsWidget::on_field_currentIndexChanged(int index)
{
if (index < 0)
return;
QVariantMap vm = field->itemData(index).toMap();
if (index) { // standard frame fields
offset->setValue(vm["offset"].toUInt());
offset->setDisabled(true);
type->setCurrentIndex(vm["type"].toUInt());
type->setDisabled(true);
bitmask->setText(uintToHexStr(
vm["mask"].toUInt(),
typeSize(OstProto::VariableField::Type(
vm["type"].toUInt()))));
bitmask->setDisabled(true);
}
else { // custom field
offset->setEnabled(true);
type->setEnabled(true);
bitmask->setEnabled(true);
}
}
void VariableFieldsWidget::on_type_currentIndexChanged(int index)
{
if (!protocolList->currentItem())
return;
AbstractProtocol *proto = protocolList->currentItem()->data(Qt::UserRole)
.value<AbstractProtocol*>();
int protoSize = proto->protocolFrameSize();
switch (index)
{
case OstProto::VariableField::kCounter8:
offset->setRange(0, protoSize - 1);
bitmask->setInputMask("HH");
bitmask->setText("FF");
valueRange_->setRange(0, 0xFF);
count->setRange(0, 0xFF);
step->setRange(0, 0xFF);
break;
case OstProto::VariableField::kCounter16:
offset->setRange(0, protoSize - 2);
bitmask->setInputMask("HHHH");
bitmask->setText("FFFF");
valueRange_->setRange(0, 0xFFFF);
count->setRange(0, 0xFFFF);
step->setRange(0, 0xFFFF);
break;
case OstProto::VariableField::kCounter32:
offset->setRange(0, protoSize - 4);
bitmask->setInputMask("HHHHHHHH");
bitmask->setText("FFFFFFFF");
valueRange_->setRange(0, 0xFFFFFFFF);
count->setRange(0, 0xFFFF);
step->setRange(0, 0xFFFF);
break;
default:
Q_ASSERT(false); // unreachable
break;
}
}
void VariableFieldsWidget::updateCurrentVariableField()
{
// Prevent recursion
if (isProgLoad_)
return;
if (!protocolList->currentItem())
return;
if (!variableFieldList->currentItem())
return;
OstProto::VariableField vf;
vf.set_type(OstProto::VariableField::Type(type->currentIndex()));
vf.set_offset(offset->value());
vf.set_mask(hexStrToUInt(bitmask->text()));
vf.set_value(value->text().toUInt());
vf.set_mode(OstProto::VariableField::Mode(mode->currentIndex()));
vf.set_count(count->value());
vf.set_step(step->value());
QListWidgetItem *protoItem = protocolList->currentItem();
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
.value<AbstractProtocol*>();
proto->mutableVariableField(variableFieldList->currentRow())->CopyFrom(vf);
setVariableFieldItem(variableFieldList->currentItem(), proto, vf);
}
void VariableFieldsWidget::loadProtocolFields(
const AbstractProtocol *protocol)
{
QVariantMap vm;
field->clear();
field->addItem("Custom");
for (int i = 0; i < protocol->frameFieldCount(); i++) {
if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
continue;
QString name = protocol->fieldData(i, AbstractProtocol::FieldName)
.toString();
int bitOfs = protocol->fieldFrameBitOffset(i);
int byteOfs = bitOfs >> 3;
uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
.toInt();
vm["offset"] = byteOfs;
if (bitSize <= 8) {
vm["type"] = int(OstProto::VariableField::kCounter8);
vm["mask"] = ((0xFF << (8 - bitSize)) & 0xFF)
>> (bitOfs & 0x7);
}
else if (bitSize <= 16) {
vm["type"] = int(OstProto::VariableField::kCounter16);
vm["mask"] = ((0xFFFF << (16 - bitSize)) & 0xFFFF)
>> (bitOfs & 0x7);
}
else if (bitSize <= 32) {
vm["type"] = int(OstProto::VariableField::kCounter32);
vm["mask"] = ((0xFFFFFFFF << (32 - bitSize)) & 0xFFFFFFFF)
>> (bitOfs & 0x7);
}
else {
vm["type"] = int(OstProto::VariableField::kCounter32);
vm["mask"] = 0xFFFFFFFF;
}
field->addItem(name, vm);
}
}
/*! Given a VariableField::Type, return corresponding size in bytes */
int VariableFieldsWidget::typeSize(OstProto::VariableField::Type type)
{
switch(type) {
case OstProto::VariableField::kCounter8 : return 1;
case OstProto::VariableField::kCounter16: return 2;
case OstProto::VariableField::kCounter32: return 4;
default: break;
}
return 4;
}
/*! Given a variableField, return corresponding index in the field ComboBox */
int VariableFieldsWidget::fieldIndex(const OstProto::VariableField &vf)
{
QVariantMap vm;
vm["type"] = int(vf.type());
vm["offset"] = vf.offset();
vm["mask"] = vf.mask();
int index = field->findData(vm);
qDebug("vm %d %d 0x%x => index %d", vf.type(), vf.offset(), vf.mask(), index);
// Not found? Use 'Custom'
if (index < 0)
index = 0;
return index;
}
void VariableFieldsWidget::setVariableFieldItem(
QListWidgetItem *item,
const AbstractProtocol *protocol,
const OstProto::VariableField &vf)
{
uint from = vf.value() & vf.mask();
uint to;
QString fieldName = field->itemText(fieldIndex(vf));
QString itemText;
if (vf.mode() == OstProto::VariableField::kDecrement)
to = (vf.value() - (vf.count()-1)*vf.step()) & vf.mask();
else
to = (vf.value() + (vf.count()-1)*vf.step()) & vf.mask();
item->setData(Qt::UserRole, QVariant::fromValue(vf));
itemText = QString("%1 %2 %3 from %4 to %5")
.arg(protocol->shortName())
.arg(fieldName)
.arg(modeNames.at(vf.mode()))
.arg(from)
.arg(to);
if (vf.step() != 1)
itemText.append(QString(" step %1").arg(vf.step()));
item->setText(itemText);
}

View File

@ -0,0 +1,72 @@
/*
Copyright (C) 2015 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 _VARIABLE_FIELDS_WIDGET_H
#define _VARIABLE_FIELDS_WIDGET_H
#include "protocol.pb.h"
#include "ui_variablefieldswidget.h"
#include <QWidget>
class AbstractProtocol;
class Stream;
class QListWidgetItem;
class VariableFieldsWidget : public QWidget, private Ui::VariableFieldsWidget
{
Q_OBJECT
public:
VariableFieldsWidget(QWidget *parent = 0);
void setStream(Stream *stream);
void load();
void store();
void clear();
private slots:
void on_protocolList_currentItemChanged(
QListWidgetItem *current,
QListWidgetItem *previous);
void on_variableFieldList_currentItemChanged(
QListWidgetItem *current,
QListWidgetItem *previous);
void on_addButton_clicked();
void on_deleteButton_clicked();
void on_field_currentIndexChanged(int index);
void on_type_currentIndexChanged(int index);
void updateCurrentVariableField();
private:
void loadProtocolFields(const AbstractProtocol *protocol);
int typeSize(OstProto::VariableField::Type type);
int fieldIndex(const OstProto::VariableField &vf);
void setVariableFieldItem(
QListWidgetItem *item,
const AbstractProtocol *protocol,
const OstProto::VariableField &vf);
Stream *stream_;
QIntValidator *valueRange_;
bool isProgLoad_;
// FIXME: make the lastXXX vars static?
int lastSelectedProtocolIndex_;
int lastSelectedVariableFieldIndex_;
};
#endif

View File

@ -0,0 +1,292 @@
<ui version="4.0" >
<class>VariableFieldsWidget</class>
<widget class="QWidget" name="VariableFieldsWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>579</width>
<height>384</height>
</rect>
</property>
<property name="windowTitle" >
<string>Form</string>
</property>
<layout class="QVBoxLayout" >
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible" >
<bool>false</bool>
</property>
<widget class="QListWidget" name="protocolList" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QListWidget" name="variableFieldList" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>6</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" >
<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>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="addButton" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="text" >
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteButton" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="text" >
<string> - </string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="attribGroup" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string/>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<string>Field</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QComboBox" name="field" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="insertPolicy" >
<enum>QComboBox::NoInsert</enum>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Type</string>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QComboBox" name="type" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="insertPolicy" >
<enum>QComboBox::NoInsert</enum>
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Offset</string>
</property>
</widget>
</item>
<item row="0" column="5" >
<widget class="QSpinBox" name="offset" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="wrapping" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="6" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>Mask</string>
</property>
<property name="buddy" >
<cstring>bitmask</cstring>
</property>
</widget>
</item>
<item row="0" column="7" >
<widget class="QLineEdit" name="bitmask" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<string>Mode</string>
</property>
<property name="buddy" >
<cstring>mode</cstring>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QComboBox" name="mode" >
<property name="insertPolicy" >
<enum>QComboBox::NoInsert</enum>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>Value</string>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLineEdit" name="value" />
</item>
<item row="1" column="4" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<string>Count</string>
</property>
</widget>
</item>
<item row="1" column="5" >
<widget class="QSpinBox" name="count" />
</item>
<item row="1" column="6" >
<widget class="QLabel" name="label_10" >
<property name="text" >
<string>Step</string>
</property>
</widget>
</item>
<item row="1" column="7" >
<widget class="QSpinBox" name="step" />
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>561</width>
<height>41</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>protocolList</tabstop>
<tabstop>variableFieldList</tabstop>
<tabstop>addButton</tabstop>
<tabstop>deleteButton</tabstop>
<tabstop>field</tabstop>
<tabstop>type</tabstop>
<tabstop>offset</tabstop>
<tabstop>bitmask</tabstop>
<tabstop>mode</tabstop>
<tabstop>value</tabstop>
<tabstop>count</tabstop>
<tabstop>step</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -53,7 +53,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
- protocolIdType()
- protocolId()
- protocolFrameSize()
- isProtocolFrameValueVariable()
- isProtocolFrameSizeVariable()
- protocolFrameVariableCount()
@ -114,6 +113,42 @@ quint32 AbstractProtocol::protocolNumber() const
return 0xFFFFFFFF;
}
/*!
Copies the common data (not specific to individual protocols) in the
protocol member protobuf data into the passed in protocol parameter.
The individual protocol specific protobuf data is copied using
protoDataCopyInto()
*/
void AbstractProtocol::commonProtoDataCopyInto(OstProto::Protocol &protocol) const
{
protocol.clear_variable_fields();
for (int i = 0; i < _data.variable_fields_size(); i++)
{
OstProto::VariableField *vf;
vf = protocol.add_variable_fields();
vf->CopyFrom(_data.variable_fields(i));
}
}
/*!
Copies the common data (not specific to individual protocols) from the
passed in param protocol protobuf into the member protobuf data.
The individual protocol specific protobuf data is copied using
protoDataCopyFrom()
*/
void AbstractProtocol::commonProtoDataCopyFrom(const OstProto::Protocol &protocol)
{
_data.clear_variable_fields();
for (int i = 0; i < protocol.variable_fields_size(); i++)
{
OstProto::VariableField *vf;
vf = _data.add_variable_fields();
vf->CopyFrom(protocol.variable_fields(i));
}
}
/*!
\fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0
@ -316,6 +351,91 @@ bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/,
return false;
}
/*!
* Returns the bit offset where the specified field starts within the
* protocolFrameValue()
*/
int AbstractProtocol::fieldFrameBitOffset(int index, int streamIndex) const
{
int ofs = 0;
if ((index < 0) || (index >= frameFieldCount()))
return -1;
// TODO: we should cache the return value
for (int i = 0; i < index; i++)
ofs += fieldData(i, FieldBitSize, streamIndex).toInt();
qDebug("======> index: %d, ofs: %d", index, ofs);
return ofs;
}
/*!
* Returns the count of variableFields in the protocol
*/
int AbstractProtocol::variableFieldCount() const
{
return _data.variable_fields_size();
}
/*!
* Appends a variableField to the protocol
*/
void AbstractProtocol::appendVariableField(const OstProto::VariableField &vf)
{
_data.add_variable_fields()->CopyFrom(vf);
}
/*!
* Removes the variableField from the protocol at the specified index
*/
void AbstractProtocol::removeVariableField(int index)
{
OstProto::Protocol temp;
if (index >= _data.variable_fields_size()) {
qWarning("%s: %s variableField[%d] out of range; count: %d)",
__FUNCTION__, qPrintable(shortName()),
index, _data.variable_fields_size());
return;
}
// TODO: this is inefficient - evaluate using RepeatedPtrField?
for (int i = 0; i < _data.variable_fields_size(); i++) {
if (i == index)
continue;
temp.add_variable_fields()->CopyFrom(_data.variable_fields(i));
}
_data.clear_variable_fields();
for (int i = 0; i < temp.variable_fields_size(); i++) {
_data.add_variable_fields()->CopyFrom(temp.variable_fields(i));
}
}
/*!
* Returns the variableField at the specified index as a constant Reference
* i.e. read-only
*/
const OstProto::VariableField& AbstractProtocol::variableField(int index) const
{
Q_ASSERT(index < _data.variable_fields_size());
return _data.variable_fields(index);
}
/*!
* Returns the variableField at the specified index as a mutable pointer.
* Changes made via the pointer will be reflected in the protocol
*/
OstProto::VariableField* AbstractProtocol::mutableVariableField(int index)
{
if ((index < 0) || (index >= _data.variable_fields_size()))
return NULL;
return _data.mutable_variable_fields(index);
}
/*!
Returns the protocolIdType for the protocol
@ -542,6 +662,13 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum)
}
}
// Overwrite proto with the variable fields, if any
for (int i = 0; i < _data.variable_fields_size(); i++)
{
OstProto::VariableField vf = _data.variable_fields(i);
varyProtocolFrameValue(proto, streamIndex, vf);
}
return proto;
}
@ -555,6 +682,7 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum)
*/
bool AbstractProtocol::isProtocolFrameValueVariable() const
{
// TODO: change subclass to call base class function?
return (protocolFrameVariableCount() > 1);
}
@ -578,14 +706,23 @@ bool AbstractProtocol::isProtocolFrameSizeVariable() const
fields in the protocol. Use the AbstractProtocol::lcm() static utility
function to calculate the LCM.
The default implementation returns 1 implying that the protocol has no
varying fields. A subclass should reimplement if it has varying fields
e.g. an IP protocol that increments/decrements the IP address with
every packet
The default implementation returns the LCM of all variableFields
A subclass should reimplement if it has varying fields e.g. an IP protocol
that increments/decrements the IP address with every packet.\n
Subclasses should call the base class method to retreive the count and
do a LCM with the subclass' own varying fields
*/
int AbstractProtocol::protocolFrameVariableCount() const
{
return 1;
// TODO: change subclass to call base class function?
int count = 1;
for (int i = 0; i < _data.variable_fields_size(); i++)
count = lcm(count, _data.variable_fields(i).count());
// TODO: cache the value!
return count;
}
/*!
@ -896,3 +1033,124 @@ quint64 AbstractProtocol::lcm(quint64 u, quint64 v)
return (u * v)/gcd(u, v);
}
void AbstractProtocol::varyProtocolFrameValue(QByteArray &buf, int frameIndex,
const OstProto::VariableField &varField) const
{
int x = frameIndex % varField.count();
// FIXME: use templates for duplicating code for quint8, quint16, quint32
switch (varField.type()) {
case OstProto::VariableField::kCounter8: {
quint8 oldfv, newfv;
if ((varField.offset() + sizeof(quint8)) > quint8(buf.size()))
{
qWarning("%s varField ofs %d beyond protocol frame %d - skipping",
qPrintable(shortName()), varField.offset(), buf.size());
goto _exit;
}
oldfv = *((uchar*)buf.constData() + varField.offset());
switch(varField.mode())
{
case OstProto::VariableField::kIncrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + x) & varField.mask());
break;
case OstProto::VariableField::kDecrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() - x) & varField.mask());
break;
case OstProto::VariableField::kRandom:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + qrand()) & varField.mask());
break;
default:
qWarning("%s Unsupported varField mode %d", qPrintable(shortName()),
varField.mode());
}
*((uchar*)buf.constData() + varField.offset()) = newfv;
qDebug("%s varField ofs %d oldfv %x newfv %x", qPrintable(shortName()),
varField.offset(), oldfv, newfv);
break;
}
case OstProto::VariableField::kCounter16: {
quint16 oldfv, newfv;
if ((varField.offset() + sizeof(quint16)) > quint16(buf.size()))
{
qWarning("%s varField ofs %d beyond protocol frame %d - skipping",
qPrintable(shortName()), varField.offset(), buf.size());
goto _exit;
}
oldfv = qFromBigEndian<quint16>((uchar*)buf.constData()
+ varField.offset());
switch(varField.mode())
{
case OstProto::VariableField::kIncrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + x) & varField.mask());
break;
case OstProto::VariableField::kDecrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() - x) & varField.mask());
break;
case OstProto::VariableField::kRandom:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + qrand()) & varField.mask());
break;
default:
qWarning("%s Unsupported varField mode %d", qPrintable(shortName()),
varField.mode());
}
qToBigEndian(newfv, (uchar*)buf.constData() + varField.offset());
qDebug("%s varField ofs %d oldfv %x newfv %x", qPrintable(shortName()),
varField.offset(), oldfv, newfv);
break;
}
case OstProto::VariableField::kCounter32: {
quint32 oldfv, newfv;
if ((varField.offset() + sizeof(quint32)) > quint32(buf.size()))
{
qWarning("%s varField ofs %d beyond protocol frame %d - skipping",
qPrintable(shortName()), varField.offset(), buf.size());
goto _exit;
}
oldfv = qFromBigEndian<quint32>((uchar*)buf.constData()
+ varField.offset());
switch(varField.mode())
{
case OstProto::VariableField::kIncrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + x) & varField.mask());
break;
case OstProto::VariableField::kDecrement:
newfv = (oldfv & ~varField.mask())
| ((varField.value() - x) & varField.mask());
break;
case OstProto::VariableField::kRandom:
newfv = (oldfv & ~varField.mask())
| ((varField.value() + qrand()) & varField.mask());
break;
default:
qWarning("%s Unsupported varField mode %d", qPrintable(shortName()),
varField.mode());
}
qToBigEndian(newfv, (uchar*)buf.constData() + varField.offset());
qDebug("%s varField ofs %d oldfv %x newfv %x", qPrintable(shortName()),
varField.offset(), oldfv, newfv);
break;
}
default:
break;
}
_exit:
return;
}

View File

@ -49,6 +49,7 @@ private:
mutable int _frameFieldCount;
mutable int protoSize;
mutable QString protoAbbr;
OstProto::Protocol _data;
protected:
StreamBase *mpStream; //!< Stream that this protocol belongs to
@ -108,6 +109,8 @@ public:
AbstractProtocol *parent = 0);
virtual quint32 protocolNumber() const;
void commonProtoDataCopyInto(OstProto::Protocol &protocol) const;
void commonProtoDataCopyFrom(const OstProto::Protocol &protocol);
virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0;
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0;
@ -127,6 +130,13 @@ public:
int streamIndex = 0) const;
virtual bool setFieldData(int index, const QVariant &value,
FieldAttrib attrib = FieldValue);
int fieldFrameBitOffset(int index, int streamIndex = 0) const;
int variableFieldCount() const;
void appendVariableField(const OstProto::VariableField &vf);
void removeVariableField(int index);
const OstProto::VariableField& variableField(int index) const;
OstProto::VariableField* mutableVariableField(int index);
QByteArray protocolFrameValue(int streamIndex = 0,
bool forCksum = false) const;
@ -154,6 +164,10 @@ public:
static quint64 lcm(quint64 u, quint64 v);
static quint64 gcd(quint64 u, quint64 v);
private:
void varyProtocolFrameValue(QByteArray &buf, int frameIndex,
const OstProto::VariableField &varField) const;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags);

View File

@ -791,18 +791,9 @@ _exit:
return isOk;
}
bool Ip4Protocol::isProtocolFrameValueVariable() const
{
if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed)
|| (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed))
return true;
else
return false;
}
int Ip4Protocol::protocolFrameVariableCount() const
{
int count = 1;
int count = AbstractProtocol::protocolFrameVariableCount();
if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed)
count = AbstractProtocol::lcm(count, data.src_ip_count());
@ -820,23 +811,21 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
{
case CksumIpPseudo:
{
quint32 sum;
quint32 sum = 0;
QByteArray fv = protocolFrameValue(streamIndex);
const char *p = fv.constData();
sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16;
sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF;
sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16;
sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF;
sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF;
sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20;
sum += *((quint16*)(p + 12)); // src-ip hi
sum += *((quint16*)(p + 14)); // src-ip lo
sum += *((quint16*)(p + 16)); // dst-ip hi
sum += *((quint16*)(p + 18)); // dst-ip lo
sum += qToBigEndian((quint16) protocolFramePayloadSize()); // len
sum += *((quint8*) (p + 9)) << 8; // proto
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
// Above calculation done assuming 'big endian'
// - so convert to host order
//return qFromBigEndian(sum);
return ~sum;
return ~qFromBigEndian((quint16)sum);
}
default:
break;

View File

@ -85,7 +85,6 @@ public:
virtual bool setFieldData(int index, const QVariant &value,
FieldAttrib attrib = FieldValue);
virtual bool isProtocolFrameValueVariable() const;
virtual int protocolFrameVariableCount() const;
virtual quint32 protocolFrameCksum(int streamIndex = 0,

View File

@ -783,30 +783,20 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
{
if (cksumType == CksumIpPseudo)
{
QByteArray addr;
quint32 sum = 0;
QByteArray fv = protocolFrameValue(streamIndex);
const char *p = fv.constData();
addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex)
.toByteArray();
Q_ASSERT(addr.size() == 16);
for (int i = 0; i < addr.size(); i+=2)
sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1));
addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex)
.toByteArray();
Q_ASSERT(addr.size() == 16);
for (int i = 0; i < addr.size(); i+=2)
sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1));
sum += fieldData(ip6_payloadLength, FieldValue, streamIndex)
.toUInt() & 0xFFFF;
sum += fieldData(ip6_nextHeader, FieldValue, streamIndex)
.toUInt() & 0xFF;
// src-ip, dst-ip
for (int i = 8; i < fv.size(); i+=2)
sum += *((quint16*)(p + i));
sum += *((quint16*)(p + 4)); // payload len
sum += *((quint8*) (p + 6)) << 8; // proto
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ~sum;
return ~qFromBigEndian((quint16)sum);
}
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
}

View File

@ -92,9 +92,32 @@ message ProtocolId {
required uint32 id = 1;
}
message VariableField {
enum Type {
kCounter8 = 0;
kCounter16 = 1;
kCounter32 = 2;
}
enum Mode {
kIncrement = 0;
kDecrement = 1;
kRandom = 2;
}
optional Type type = 1 [default = kCounter8];
optional uint32 offset = 2;
optional fixed32 mask = 3 [default = 0xffffffff];
optional uint32 value = 4;
optional Mode mode = 5 [default = kIncrement];
optional uint32 count = 6 [default = 16];
optional uint32 step = 7 [default = 1];
}
message Protocol {
required ProtocolId protocol_id = 1;
repeated VariableField variable_fields = 2;
extensions 100 to 199; // Reserved for Ostinato Use
extensions 200 to 500; // Available for use by protocols

View File

@ -99,6 +99,7 @@ void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream)
continue;
}
proto = OstProtocolManager->createProtocol(protoId, this);
proto->commonProtoDataCopyFrom(stream.protocol(i));
proto->protoDataCopyFrom(stream.protocol(i));
iter->insert(proto);
}
@ -118,6 +119,7 @@ void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const
OstProto::Protocol *p;
p = stream.add_protocol();
proto->commonProtoDataCopyInto(*p);
proto->protoDataCopyInto(*p);
}
}

253
test/vftest.py Normal file
View File

@ -0,0 +1,253 @@
#! /usr/bin/env python
# standard modules
import logging
import os
import subprocess
import sys
import time
sys.path.insert(1, '../binding')
from core import ost_pb, DroneProxy
from rpc import RpcError
from protocols.mac_pb2 import mac
from protocols.ip4_pb2 import ip4, Ip4
from protocols.ip6_pb2 import ip6, Ip6
class Test:
pass
class TestSuite:
def __init__(self):
self.results = []
self.total = 0
self.passed = 0
self.completed = False
def test_begin(self, name):
test = Test()
test.name = name
test.passed = False
self.running = test
print('-----------------------------------------------------------')
print('@@TEST: %s' % name)
print('-----------------------------------------------------------')
def test_end(self, result):
if self.running:
self.running.passed = result
self.results.append(self.running)
self.total = self.total + 1
if result:
self.passed = self.passed + 1
self.running = None
print('@@RESULT: %s' % ('PASS' if result else 'FAIL'))
else:
raise Exception('Test end without a test begin')
def report(self):
print('===========================================================')
print('TEST REPORT')
print('===========================================================')
for test in self.results:
print('%s: %d' % (test.name, test.passed))
print('Passed: %d/%d' % (self.passed, self.total))
print('Completed: %d' % (self.completed))
def complete(self):
self.completed = True
def passed(self):
return passed == total and self.completed
# initialize defaults
host_name = '127.0.0.1'
tx_port_number = -1
rx_port_number = -1
drone_version = ['0', '0', '0']
if sys.platform == 'win32':
tshark = r'C:\Program Files\Wireshark\tshark.exe'
else:
tshark = 'tshark'
# setup logging
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
print('')
print('This test uses the following topology -')
print('')
print(' +-------+ ')
print(' | |Tx--->----+')
print(' | Drone | |')
print(' | |Rx---<----+')
print(' +-------+ ')
print('')
print('A loopback port is used as both the Tx and Rx ports')
print('')
suite = TestSuite()
drone = DroneProxy(host_name)
try:
# ----------------------------------------------------------------- #
# Baseline Configuration for subsequent testcases
# ----------------------------------------------------------------- #
# connect to drone
log.info('connecting to drone(%s:%d)'
% (drone.hostName(), drone.portNumber()))
drone.connect()
# retreive port id list
log.info('retreiving port list')
port_id_list = drone.getPortIdList()
# retreive port config list
log.info('retreiving port config for all ports')
port_config_list = drone.getPortConfig(port_id_list)
if len(port_config_list.port) == 0:
log.warning('drone has no ports!')
sys.exit(1)
# iterate port list to find a loopback port to use as the tx/rx port id
print('Port List')
print('---------')
for port in port_config_list.port:
print('%d.%s (%s)' % (port.port_id.id, port.name, port.description))
# use a loopback port as default tx/rx port
if ('lo' in port.name or 'loopback' in port.description.lower()):
tx_port_number = port.port_id.id
rx_port_number = port.port_id.id
if tx_port_number < 0 or rx_port_number < 0:
log.warning('loopback port not found')
sys.exit(1)
print('Using port %d as tx/rx port(s)' % tx_port_number)
tx_port = ost_pb.PortIdList()
tx_port.port_id.add().id = tx_port_number;
rx_port = ost_pb.PortIdList()
rx_port.port_id.add().id = rx_port_number;
# ----------------------------------------------------------------- #
# TESTCASE: Verify counter16 variable field not affecting any
# checksums - vlan-id
# ----------------------------------------------------------------- #
# add a stream
stream_id = ost_pb.StreamIdList()
stream_id.port_id.CopyFrom(tx_port.port_id[0])
stream_id.stream_id.add().id = 1
log.info('adding tx_stream %d' % stream_id.stream_id[0].id)
drone.addStream(stream_id)
# configure the stream
stream_cfg = ost_pb.StreamConfigList()
stream_cfg.port_id.CopyFrom(tx_port.port_id[0])
s = stream_cfg.stream.add()
s.stream_id.id = stream_id.stream_id[0].id
s.core.is_enabled = True
s.core.frame_len = 128
s.control.num_packets = 10
# setup stream protocols as mac:vlan:eth2:ip4:udp:payload
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
p.Extensions[mac].dst_mac = 0x001122334455
p.Extensions[mac].src_mac = 0x00aabbccddee
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kVlanFieldNumber
vf = p.variable_fields.add()
vf.type = ost_pb.VariableField.kCounter16
vf.offset = 2
vf.mask = 0x0fff
vf.value = 101
vf.mode = ost_pb.VariableField.kDecrement
vf.count = 7
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
#reduce typing by creating a shorter reference to p.Extensions[ip4]
ip = p.Extensions[ip4]
ip.src_ip = 0x01020304
ip.dst_ip = 0x05060708
# p = s.protocol.add()
# p.protocol_id.id = ost_pb.Protocol.kIp6FieldNumber
# ip = p.Extensions[ip6]
# ip.src_addr_hi = 0x2002000000000000
# ip.src_addr_lo = 0x1001
# ip.dst_addr_hi = 0x4004000000000000
# ip.dst_addr_lo = 0x1001
vf = p.variable_fields.add()
vf.type = ost_pb.VariableField.kCounter32
vf.offset = 16 # dst-ip4
#vf.offset = 20 # src-ip6
vf.mask = 0x000000ff
vf.value = 101
vf.mode = ost_pb.VariableField.kIncrement
vf.count = 5
s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
log.info('configuring tx_stream %d' % stream_id.stream_id[0].id)
drone.modifyStream(stream_cfg)
# clear tx/rx stats
log.info('clearing tx/rx stats')
drone.clearStats(tx_port)
drone.clearStats(rx_port)
passed = False
suite.test_begin('counter16NotAffectingCksums')
try:
drone.startCapture(rx_port)
drone.startTransmit(tx_port)
log.info('waiting for transmit to finish ...')
time.sleep(12)
drone.stopTransmit(tx_port)
drone.stopCapture(rx_port)
log.info('getting Rx capture buffer')
buff = drone.getCaptureBuffer(rx_port.port_id[0])
drone.saveCaptureBuffer(buff, 'capture.pcap')
log.info('dumping Rx capture buffer')
cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap'])
print(cap_pkts)
if '5.6.7.8' in cap_pkts and '5.6.7.17' in cap_pkts:
passed = True
os.remove('capture.pcap')
except RpcError as e:
raise
finally:
drone.stopTransmit(tx_port)
suite.test_end(passed)
suite.complete()
# delete streams
log.info('deleting tx_stream %d' % stream_id.stream_id[0].id)
drone.deleteStream(stream_id)
# bye for now
drone.disconnect()
except Exception as ex:
log.exception(ex)
finally:
suite.report()
if not suite.passed:
sys.exit(2);