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:
parent
77fe49bf10
commit
df24cf8b15
@ -52,7 +52,8 @@ HEADERS += \
|
|||||||
streamconfigdialog.h \
|
streamconfigdialog.h \
|
||||||
streamlistdelegate.h \
|
streamlistdelegate.h \
|
||||||
streammodel.h \
|
streammodel.h \
|
||||||
updater.h
|
updater.h \
|
||||||
|
variablefieldswidget.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
about.ui \
|
about.ui \
|
||||||
@ -62,7 +63,8 @@ FORMS += \
|
|||||||
portstatswindow.ui \
|
portstatswindow.ui \
|
||||||
portswindow.ui \
|
portswindow.ui \
|
||||||
preferences.ui \
|
preferences.ui \
|
||||||
streamconfigdialog.ui
|
streamconfigdialog.ui \
|
||||||
|
variablefieldswidget.ui
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
dumpview.cpp \
|
dumpview.cpp \
|
||||||
@ -84,7 +86,8 @@ SOURCES += \
|
|||||||
streamconfigdialog.cpp \
|
streamconfigdialog.cpp \
|
||||||
streamlistdelegate.cpp \
|
streamlistdelegate.cpp \
|
||||||
streammodel.cpp \
|
streammodel.cpp \
|
||||||
updater.cpp
|
updater.cpp \
|
||||||
|
variablefieldswidget.cpp
|
||||||
|
|
||||||
|
|
||||||
QMAKE_DISTCLEAN += object_script.*
|
QMAKE_DISTCLEAN += object_script.*
|
||||||
|
@ -151,6 +151,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
|||||||
this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&,
|
this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&,
|
||||||
const QModelIndex&)));
|
const QModelIndex&)));
|
||||||
|
|
||||||
|
variableFieldsWidget->setStream(mpStream);
|
||||||
|
|
||||||
LoadCurrentStream();
|
LoadCurrentStream();
|
||||||
mpPacketModel = new PacketModel(this);
|
mpPacketModel = new PacketModel(this);
|
||||||
tvPacketTree->setModel(mpPacketModel);
|
tvPacketTree->setModel(mpPacketModel);
|
||||||
@ -644,15 +646,26 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream Control
|
// Variable Fields
|
||||||
case 2:
|
case 2:
|
||||||
|
{
|
||||||
|
StoreCurrentStream();
|
||||||
|
|
||||||
|
// Stream protocols may have changed - clear and reload
|
||||||
|
variableFieldsWidget->clear();
|
||||||
|
variableFieldsWidget->load();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream Control
|
||||||
|
case 3:
|
||||||
{
|
{
|
||||||
StoreCurrentStream();
|
StoreCurrentStream();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet View
|
// Packet View
|
||||||
case 3:
|
case 4:
|
||||||
{
|
{
|
||||||
StoreCurrentStream();
|
StoreCurrentStream();
|
||||||
mpPacketModel->setSelectedProtocols(*_iter);
|
mpPacketModel->setSelectedProtocols(*_iter);
|
||||||
@ -964,6 +977,11 @@ void StreamConfigDialog::LoadCurrentStream()
|
|||||||
loadProtocolWidgets();
|
loadProtocolWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variable Fields
|
||||||
|
{
|
||||||
|
variableFieldsWidget->load();
|
||||||
|
}
|
||||||
|
|
||||||
// Stream Control
|
// Stream Control
|
||||||
{
|
{
|
||||||
switch (mpStream->sendUnit())
|
switch (mpStream->sendUnit())
|
||||||
@ -1039,6 +1057,11 @@ void StreamConfigDialog::StoreCurrentStream()
|
|||||||
storeProtocolWidgets();
|
storeProtocolWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variable Fields
|
||||||
|
{
|
||||||
|
variableFieldsWidget->store();
|
||||||
|
}
|
||||||
|
|
||||||
// Stream Control
|
// Stream Control
|
||||||
{
|
{
|
||||||
if (rbSendPackets->isChecked())
|
if (rbSendPackets->isChecked())
|
||||||
|
@ -767,6 +767,16 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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" >
|
<widget class="QWidget" name="streamControlTab" >
|
||||||
<attribute name="title" >
|
<attribute name="title" >
|
||||||
<string>Stream Control</string>
|
<string>Stream Control</string>
|
||||||
@ -1209,6 +1219,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<header>dumpview.h</header>
|
<header>dumpview.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>VariableFieldsWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>variablefieldswidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>twTopLevel</tabstop>
|
<tabstop>twTopLevel</tabstop>
|
||||||
|
431
client/variablefieldswidget.cpp
Normal file
431
client/variablefieldswidget.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
72
client/variablefieldswidget.h
Normal file
72
client/variablefieldswidget.h
Normal 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
|
292
client/variablefieldswidget.ui
Normal file
292
client/variablefieldswidget.ui
Normal 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>
|
@ -53,7 +53,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
- protocolIdType()
|
- protocolIdType()
|
||||||
- protocolId()
|
- protocolId()
|
||||||
- protocolFrameSize()
|
- protocolFrameSize()
|
||||||
- isProtocolFrameValueVariable()
|
|
||||||
- isProtocolFrameSizeVariable()
|
- isProtocolFrameSizeVariable()
|
||||||
- protocolFrameVariableCount()
|
- protocolFrameVariableCount()
|
||||||
|
|
||||||
@ -114,6 +113,42 @@ quint32 AbstractProtocol::protocolNumber() const
|
|||||||
return 0xFFFFFFFF;
|
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
|
\fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0
|
||||||
|
|
||||||
@ -316,6 +351,91 @@ bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/,
|
|||||||
return false;
|
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
|
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;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,6 +682,7 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum)
|
|||||||
*/
|
*/
|
||||||
bool AbstractProtocol::isProtocolFrameValueVariable() const
|
bool AbstractProtocol::isProtocolFrameValueVariable() const
|
||||||
{
|
{
|
||||||
|
// TODO: change subclass to call base class function?
|
||||||
return (protocolFrameVariableCount() > 1);
|
return (protocolFrameVariableCount() > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,14 +706,23 @@ bool AbstractProtocol::isProtocolFrameSizeVariable() const
|
|||||||
fields in the protocol. Use the AbstractProtocol::lcm() static utility
|
fields in the protocol. Use the AbstractProtocol::lcm() static utility
|
||||||
function to calculate the LCM.
|
function to calculate the LCM.
|
||||||
|
|
||||||
The default implementation returns 1 implying that the protocol has no
|
The default implementation returns the LCM of all variableFields
|
||||||
varying fields. A subclass should reimplement if it has varying fields
|
A subclass should reimplement if it has varying fields e.g. an IP protocol
|
||||||
e.g. an IP protocol that increments/decrements the IP address with
|
that increments/decrements the IP address with every packet.\n
|
||||||
every packet
|
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
|
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);
|
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;
|
||||||
|
}
|
||||||
|
@ -49,6 +49,7 @@ private:
|
|||||||
mutable int _frameFieldCount;
|
mutable int _frameFieldCount;
|
||||||
mutable int protoSize;
|
mutable int protoSize;
|
||||||
mutable QString protoAbbr;
|
mutable QString protoAbbr;
|
||||||
|
OstProto::Protocol _data;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
StreamBase *mpStream; //!< Stream that this protocol belongs to
|
StreamBase *mpStream; //!< Stream that this protocol belongs to
|
||||||
@ -108,6 +109,8 @@ public:
|
|||||||
AbstractProtocol *parent = 0);
|
AbstractProtocol *parent = 0);
|
||||||
virtual quint32 protocolNumber() const;
|
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 protoDataCopyInto(OstProto::Protocol &protocol) const = 0;
|
||||||
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0;
|
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0;
|
||||||
|
|
||||||
@ -127,6 +130,13 @@ public:
|
|||||||
int streamIndex = 0) const;
|
int streamIndex = 0) const;
|
||||||
virtual bool setFieldData(int index, const QVariant &value,
|
virtual bool setFieldData(int index, const QVariant &value,
|
||||||
FieldAttrib attrib = FieldValue);
|
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,
|
QByteArray protocolFrameValue(int streamIndex = 0,
|
||||||
bool forCksum = false) const;
|
bool forCksum = false) const;
|
||||||
@ -154,6 +164,10 @@ public:
|
|||||||
|
|
||||||
static quint64 lcm(quint64 u, quint64 v);
|
static quint64 lcm(quint64 u, quint64 v);
|
||||||
static quint64 gcd(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);
|
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags);
|
||||||
|
|
||||||
|
@ -791,18 +791,9 @@ _exit:
|
|||||||
return isOk;
|
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 Ip4Protocol::protocolFrameVariableCount() const
|
||||||
{
|
{
|
||||||
int count = 1;
|
int count = AbstractProtocol::protocolFrameVariableCount();
|
||||||
|
|
||||||
if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed)
|
if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed)
|
||||||
count = AbstractProtocol::lcm(count, data.src_ip_count());
|
count = AbstractProtocol::lcm(count, data.src_ip_count());
|
||||||
@ -820,23 +811,21 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex,
|
|||||||
{
|
{
|
||||||
case CksumIpPseudo:
|
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 += *((quint16*)(p + 12)); // src-ip hi
|
||||||
sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF;
|
sum += *((quint16*)(p + 14)); // src-ip lo
|
||||||
sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16;
|
sum += *((quint16*)(p + 16)); // dst-ip hi
|
||||||
sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF;
|
sum += *((quint16*)(p + 18)); // dst-ip lo
|
||||||
|
sum += qToBigEndian((quint16) protocolFramePayloadSize()); // len
|
||||||
sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF;
|
sum += *((quint8*) (p + 9)) << 8; // proto
|
||||||
sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20;
|
|
||||||
|
|
||||||
while(sum>>16)
|
while(sum>>16)
|
||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
// Above calculation done assuming 'big endian'
|
return ~qFromBigEndian((quint16)sum);
|
||||||
// - so convert to host order
|
|
||||||
//return qFromBigEndian(sum);
|
|
||||||
return ~sum;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -85,7 +85,6 @@ public:
|
|||||||
virtual bool setFieldData(int index, const QVariant &value,
|
virtual bool setFieldData(int index, const QVariant &value,
|
||||||
FieldAttrib attrib = FieldValue);
|
FieldAttrib attrib = FieldValue);
|
||||||
|
|
||||||
virtual bool isProtocolFrameValueVariable() const;
|
|
||||||
virtual int protocolFrameVariableCount() const;
|
virtual int protocolFrameVariableCount() const;
|
||||||
|
|
||||||
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
virtual quint32 protocolFrameCksum(int streamIndex = 0,
|
||||||
|
@ -783,30 +783,20 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex,
|
|||||||
{
|
{
|
||||||
if (cksumType == CksumIpPseudo)
|
if (cksumType == CksumIpPseudo)
|
||||||
{
|
{
|
||||||
QByteArray addr;
|
|
||||||
quint32 sum = 0;
|
quint32 sum = 0;
|
||||||
|
QByteArray fv = protocolFrameValue(streamIndex);
|
||||||
|
const char *p = fv.constData();
|
||||||
|
|
||||||
addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex)
|
// src-ip, dst-ip
|
||||||
.toByteArray();
|
for (int i = 8; i < fv.size(); i+=2)
|
||||||
Q_ASSERT(addr.size() == 16);
|
sum += *((quint16*)(p + i));
|
||||||
for (int i = 0; i < addr.size(); i+=2)
|
sum += *((quint16*)(p + 4)); // payload len
|
||||||
sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1));
|
sum += *((quint8*) (p + 6)) << 8; // proto
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
while(sum>>16)
|
while(sum>>16)
|
||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
return ~sum;
|
return ~qFromBigEndian((quint16)sum);
|
||||||
}
|
}
|
||||||
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
|
return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType);
|
||||||
}
|
}
|
||||||
|
@ -92,9 +92,32 @@ message ProtocolId {
|
|||||||
required uint32 id = 1;
|
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 {
|
message Protocol {
|
||||||
|
|
||||||
required ProtocolId protocol_id = 1;
|
required ProtocolId protocol_id = 1;
|
||||||
|
repeated VariableField variable_fields = 2;
|
||||||
|
|
||||||
extensions 100 to 199; // Reserved for Ostinato Use
|
extensions 100 to 199; // Reserved for Ostinato Use
|
||||||
extensions 200 to 500; // Available for use by protocols
|
extensions 200 to 500; // Available for use by protocols
|
||||||
|
@ -99,6 +99,7 @@ void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
proto = OstProtocolManager->createProtocol(protoId, this);
|
proto = OstProtocolManager->createProtocol(protoId, this);
|
||||||
|
proto->commonProtoDataCopyFrom(stream.protocol(i));
|
||||||
proto->protoDataCopyFrom(stream.protocol(i));
|
proto->protoDataCopyFrom(stream.protocol(i));
|
||||||
iter->insert(proto);
|
iter->insert(proto);
|
||||||
}
|
}
|
||||||
@ -118,6 +119,7 @@ void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const
|
|||||||
OstProto::Protocol *p;
|
OstProto::Protocol *p;
|
||||||
|
|
||||||
p = stream.add_protocol();
|
p = stream.add_protocol();
|
||||||
|
proto->commonProtoDataCopyInto(*p);
|
||||||
proto->protoDataCopyInto(*p);
|
proto->protoDataCopyInto(*p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
253
test/vftest.py
Normal file
253
test/vftest.py
Normal 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);
|
Loading…
Reference in New Issue
Block a user