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 \
|
||||
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.*
|
||||
|
@ -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())
|
||||
|
@ -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>
|
||||
|
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()
|
||||
- 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
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