Merge branch 'master' into sign
This commit is contained in:
commit
6dd6511269
@ -15,7 +15,7 @@ matrix:
|
|||||||
compiler: gcc
|
compiler: gcc
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then brew update && brew tap cartr/qt4 && brew tap-pin cartr/qt4 && brew install qt && brew install protobuf; fi"
|
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then brew update && brew tap cartr/qt4 && brew tap-pin cartr/qt4 && brew install qt@4 && brew install protobuf && ls -lR /usr/local/include; fi"
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://travis-ci.org/pstavirs/ostinato)
|
[](https://travis-ci.org/pstavirs/ostinato)
|
||||||
|
|
||||||
Ostinato is an open-source, cross-platform network packet crafter/traffic generator and analyzer with a friendly GUI. Craft and send packets of several streams with different protocols at different rates.
|
Ostinato is an open-source, cross-platform network packet crafter/traffic generator and analyzer with a friendly GUI and powerful python API. Craft and send packets of several streams with different protocols at different rates.
|
||||||
|
|
||||||
Ostinato aims to be "Wireshark in Reverse" and become complementary to Wireshark.
|
Ostinato aims to be "Wireshark in Reverse" and become complementary to Wireshark.
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
"""
|
||||||
|
This is the core module for the Ostinato Python API.
|
||||||
|
All drone configuration is done by creating an instance of the
|
||||||
|
`DroneProxy` class and calling its various methods subsequently.
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError
|
from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError
|
||||||
@ -22,8 +27,16 @@ import protocols.emulproto_pb2 as emul
|
|||||||
from __init__ import __version__
|
from __init__ import __version__
|
||||||
|
|
||||||
class DroneProxy(object):
|
class DroneProxy(object):
|
||||||
|
"""
|
||||||
|
DroneProxy acts as a proxy to a Drone instance. A method invoked on this
|
||||||
|
class will be trigerred on the actual Drone instance being proxied
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, host_name, port_number=7878):
|
def __init__(self, host_name, port_number=7878):
|
||||||
|
"""
|
||||||
|
Create a DroneProxy object as a proxy to the Drone instance
|
||||||
|
running at the specified host and port
|
||||||
|
"""
|
||||||
self.host = host_name
|
self.host = host_name
|
||||||
self.port = port_number
|
self.port = port_number
|
||||||
self.channel = OstinatoRpcChannel()
|
self.channel = OstinatoRpcChannel()
|
||||||
@ -34,14 +47,26 @@ class DroneProxy(object):
|
|||||||
fn = lambda request=self.void, method_name=method.name: \
|
fn = lambda request=self.void, method_name=method.name: \
|
||||||
self.callRpcMethod(method_name, request)
|
self.callRpcMethod(method_name, request)
|
||||||
self.__dict__[method.name] = fn
|
self.__dict__[method.name] = fn
|
||||||
|
self.__dict__[method.name].__doc__ = 'This is a protobuf API'
|
||||||
|
|
||||||
def hostName(self):
|
def hostName(self):
|
||||||
|
"""
|
||||||
|
Returns the hostname of the Drone which is being proxied by
|
||||||
|
this DroneProxy object
|
||||||
|
"""
|
||||||
return self.host
|
return self.host
|
||||||
|
|
||||||
def portNumber(self):
|
def portNumber(self):
|
||||||
|
"""
|
||||||
|
Returns the TCP port number of the Drone which is being proxied by
|
||||||
|
this DroneProxy object
|
||||||
|
"""
|
||||||
return self.port
|
return self.port
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
|
"""
|
||||||
|
Connect to the Drone instance
|
||||||
|
"""
|
||||||
self.channel.connect(self.host, self.port)
|
self.channel.connect(self.host, self.port)
|
||||||
ver = ost_pb.VersionInfo()
|
ver = ost_pb.VersionInfo()
|
||||||
ver.client_name = 'python-ostinato'
|
ver.client_name = 'python-ostinato'
|
||||||
@ -52,6 +77,9 @@ class DroneProxy(object):
|
|||||||
(ver.version, compat.notes))
|
(ver.version, compat.notes))
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
"""
|
||||||
|
Disconnect from the Drone instance
|
||||||
|
"""
|
||||||
self.channel.disconnect()
|
self.channel.disconnect()
|
||||||
|
|
||||||
def callRpcMethod(self, method_name, request):
|
def callRpcMethod(self, method_name, request):
|
||||||
@ -61,6 +89,9 @@ class DroneProxy(object):
|
|||||||
return controller.response
|
return controller.response
|
||||||
|
|
||||||
def saveCaptureBuffer(self, buffer, file_name):
|
def saveCaptureBuffer(self, buffer, file_name):
|
||||||
|
"""
|
||||||
|
Save the capture buffer in a PCAP file
|
||||||
|
"""
|
||||||
f= open(file_name, 'wb')
|
f= open(file_name, 'wb')
|
||||||
f.write(buffer)
|
f.write(buffer)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
@ -170,13 +170,13 @@ void DevicesWidget::on_deviceInfo_toggled(bool checked)
|
|||||||
|
|
||||||
void DevicesWidget::on_actionNewDeviceGroup_triggered()
|
void DevicesWidget::on_actionNewDeviceGroup_triggered()
|
||||||
{
|
{
|
||||||
// In case nothing is selected, insert 1 row at the top
|
|
||||||
int row = 0, count = 1;
|
|
||||||
QItemSelection selection = deviceGroupList->selectionModel()->selection();
|
|
||||||
|
|
||||||
if (!portGroups_)
|
if (!portGroups_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// In case nothing is selected, insert 1 row at the end
|
||||||
|
int row = portGroups_->getDeviceGroupModel()->rowCount(), count = 1;
|
||||||
|
QItemSelection selection = deviceGroupList->selectionModel()->selection();
|
||||||
|
|
||||||
// In case we have a single range selected; insert as many rows as
|
// In case we have a single range selected; insert as many rows as
|
||||||
// in the singe selected range before the top of the selected range
|
// in the singe selected range before the top of the selected range
|
||||||
if (selection.size() == 1) {
|
if (selection.size() == 1) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>DevicesWidget</class>
|
<class>DevicesWidget</class>
|
||||||
<widget class="QWidget" name="DevicesWidget">
|
<widget class="QWidget" name="DevicesWidget">
|
||||||
@ -13,16 +14,7 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<property name="leftMargin" >
|
<property name="margin">
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin" >
|
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -49,7 +41,7 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>131</width>
|
<width>131</width>
|
||||||
<height>23</height>
|
<height>23</height>
|
||||||
@ -69,17 +61,25 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/refresh.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/refresh.png</normaloff>:/icons/refresh.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="deviceGroupList" >
|
<widget class="XTableView" name="deviceGroupList">
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::ActionsContextMenu</enum>
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>This is the device group list for the selected port
|
||||||
|
|
||||||
|
A device group is a set of one or more devices/hosts which will be emulated by Ostinato
|
||||||
|
|
||||||
|
Right-click to create/edit a device group</string>
|
||||||
|
</property>
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<enum>QFrame::StyledPanel</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -95,20 +95,28 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="deviceList" >
|
<widget class="XTableView" name="deviceList">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>1</verstretch>
|
<verstretch>1</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>No devices being emulated
|
||||||
|
|
||||||
|
To emulate a device, click on Configuration and create a device group</string>
|
||||||
|
</property>
|
||||||
<property name="selectionBehavior">
|
<property name="selectionBehavior">
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="deviceDetail" >
|
<widget class="XTableView" name="deviceDetail">
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>IP neighbor cache is empty</string>
|
||||||
|
</property>
|
||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::SingleSelection</enum>
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -117,7 +125,8 @@
|
|||||||
</layout>
|
</layout>
|
||||||
<action name="actionNewDeviceGroup">
|
<action name="actionNewDeviceGroup">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_add.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/devicegroup_add.png</normaloff>:/icons/devicegroup_add.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>New Device Group</string>
|
<string>New Device Group</string>
|
||||||
@ -125,7 +134,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDeleteDeviceGroup">
|
<action name="actionDeleteDeviceGroup">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_delete.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/devicegroup_delete.png</normaloff>:/icons/devicegroup_delete.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Delete Device Group</string>
|
<string>Delete Device Group</string>
|
||||||
@ -133,13 +143,21 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionEditDeviceGroup">
|
<action name="actionEditDeviceGroup">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_edit.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/devicegroup_edit.png</normaloff>:/icons/devicegroup_edit.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Edit Device Group</string>
|
<string>Edit Device Group</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>XTableView</class>
|
||||||
|
<extends>QTableView</extends>
|
||||||
|
<header>xtableview.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="ostinato.qrc"/>
|
<include location="ostinato.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -48,7 +48,7 @@ private:
|
|||||||
void populateDump(QByteArray &dump, int &selOfs, int &selSize,
|
void populateDump(QByteArray &dump, int &selOfs, int &selSize,
|
||||||
QModelIndex parent = QModelIndex());
|
QModelIndex parent = QModelIndex());
|
||||||
bool inline isPrintable(char c)
|
bool inline isPrintable(char c)
|
||||||
{if ((c > 48) && (c < 126)) return true; else return false; }
|
{if ((c >= 32) && (c <= 126)) return true; else return false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QRect mOffsetPaneTopRect;
|
QRect mOffsetPaneTopRect;
|
||||||
|
BIN
client/icons/help.png
Normal file
BIN
client/icons/help.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 786 B |
38
client/jumpurl.h
Normal file
38
client/jumpurl.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2017 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 _JUMP_URL_H
|
||||||
|
#define _JUMP_URL_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
inline QString jumpUrl(
|
||||||
|
QString keyword,
|
||||||
|
QString source="app",
|
||||||
|
QString medium="hint",
|
||||||
|
QString name="help")
|
||||||
|
{
|
||||||
|
return QString("http://jump.ostinato.org/" + keyword + "?"
|
||||||
|
+ "utm_source=" + source + "&"
|
||||||
|
+ "utm_medium=" + medium + "&"
|
||||||
|
+ "utm_campaign=" + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "../common/ostprotolib.h"
|
#include "../common/ostprotolib.h"
|
||||||
#include "../common/protocolmanager.h"
|
#include "../common/protocolmanager.h"
|
||||||
#include "../common/protocolwidgetfactory.h"
|
#include "../common/protocolwidgetfactory.h"
|
||||||
|
#include "params.h"
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ extern const char* revision;
|
|||||||
extern ProtocolManager *OstProtocolManager;
|
extern ProtocolManager *OstProtocolManager;
|
||||||
extern ProtocolWidgetFactory *OstProtocolWidgetFactory;
|
extern ProtocolWidgetFactory *OstProtocolWidgetFactory;
|
||||||
|
|
||||||
|
Params appParams;
|
||||||
QSettings *appSettings;
|
QSettings *appSettings;
|
||||||
QMainWindow *mainWindow;
|
QMainWindow *mainWindow;
|
||||||
|
|
||||||
@ -50,6 +52,8 @@ int main(int argc, char* argv[])
|
|||||||
app.setProperty("version", version);
|
app.setProperty("version", version);
|
||||||
app.setProperty("revision", revision);
|
app.setProperty("revision", revision);
|
||||||
|
|
||||||
|
appParams.parseCommandLine(argc, argv);
|
||||||
|
|
||||||
OstProtocolManager = new ProtocolManager();
|
OstProtocolManager = new ProtocolManager();
|
||||||
OstProtocolWidgetFactory = new ProtocolWidgetFactory();
|
OstProtocolWidgetFactory = new ProtocolWidgetFactory();
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "dbgthread.h"
|
#include "dbgthread.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "jumpurl.h"
|
||||||
|
#include "params.h"
|
||||||
#include "portgrouplist.h"
|
#include "portgrouplist.h"
|
||||||
#include "portstatswindow.h"
|
#include "portstatswindow.h"
|
||||||
#include "portswindow.h"
|
#include "portswindow.h"
|
||||||
@ -40,8 +42,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#define WIN32_NO_STATUS
|
||||||
|
#include <windows.h>
|
||||||
|
#undef WIN32_NO_STATUS
|
||||||
|
#include <ntstatus.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
extern const char* version;
|
extern const char* version;
|
||||||
extern const char* revision;
|
extern const char* revision;
|
||||||
|
|
||||||
@ -50,23 +60,33 @@ PortGroupList *pgl;
|
|||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow (parent)
|
: QMainWindow (parent)
|
||||||
{
|
{
|
||||||
QString serverApp = QCoreApplication::applicationDirPath();
|
|
||||||
Updater *updater = new Updater();
|
Updater *updater = new Updater();
|
||||||
|
|
||||||
|
if (appParams.optLocalDrone()) {
|
||||||
|
QString serverApp = QCoreApplication::applicationDirPath();
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// applicationDirPath() does not return bundle, but executable inside bundle
|
// applicationDirPath() does not return bundle,
|
||||||
|
// but executable inside bundle
|
||||||
serverApp.replace("Ostinato.app", "drone.app");
|
serverApp.replace("Ostinato.app", "drone.app");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
serverApp.append("/drone.exe");
|
serverApp.append("/drone.exe");
|
||||||
#else
|
#else
|
||||||
serverApp.append("/drone");
|
serverApp.append("/drone");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
qDebug("staring local server - %s", qPrintable(serverApp));
|
||||||
localServer_ = new QProcess(this);
|
localServer_ = new QProcess(this);
|
||||||
|
connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||||
|
SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||||
|
connect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||||
|
SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||||
localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
|
localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
localServer_->start(serverApp, QStringList());
|
localServer_->start(serverApp, QStringList());
|
||||||
|
QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
localServer_ = NULL;
|
||||||
|
|
||||||
pgl = new PortGroupList;
|
pgl = new PortGroupList;
|
||||||
|
|
||||||
@ -114,6 +134,16 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
connect(updater, SIGNAL(newVersionAvailable(QString)),
|
connect(updater, SIGNAL(newVersionAvailable(QString)),
|
||||||
this, SLOT(onNewVersion(QString)));
|
this, SLOT(onNewVersion(QString)));
|
||||||
updater->checkForNewVersion();
|
updater->checkForNewVersion();
|
||||||
|
|
||||||
|
if (appParams.argumentCount()) {
|
||||||
|
QString fileName = appParams.argument(0);
|
||||||
|
if (QFile::exists(fileName))
|
||||||
|
on_actionOpenSession_triggered(fileName);
|
||||||
|
else
|
||||||
|
QMessageBox::information(NULL, qApp->applicationName(),
|
||||||
|
QString("File not found: " + fileName));
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
{
|
{
|
||||||
DbgThread *dbg = new DbgThread(pgl);
|
DbgThread *dbg = new DbgThread(pgl);
|
||||||
@ -124,12 +154,15 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
stopLocalServerMonitor();
|
||||||
|
if (localServer_) {
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
//! \todo - find a way to terminate cleanly
|
//! \todo - find a way to terminate cleanly
|
||||||
localServer_->kill();
|
localServer_->kill();
|
||||||
#else
|
#else
|
||||||
localServer_->terminate();
|
localServer_->terminate();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
delete pgl;
|
delete pgl;
|
||||||
|
|
||||||
@ -144,22 +177,26 @@ MainWindow::~MainWindow()
|
|||||||
appSettings->setValue(kApplicationWindowLayout, layout);
|
appSettings->setValue(kApplicationWindowLayout, layout);
|
||||||
appSettings->setValue(kApplicationWindowGeometryKey, geometry());
|
appSettings->setValue(kApplicationWindowGeometryKey, geometry());
|
||||||
|
|
||||||
|
if (localServer_) {
|
||||||
localServer_->waitForFinished();
|
localServer_->waitForFinished();
|
||||||
delete localServer_;
|
delete localServer_;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionOpenSession_triggered()
|
void MainWindow::on_actionOpenSession_triggered(QString fileName)
|
||||||
{
|
{
|
||||||
qDebug("Open Session Action");
|
qDebug("Open Session Action (%s)", qPrintable(fileName));
|
||||||
|
|
||||||
static QString dirName;
|
static QString dirName;
|
||||||
QString fileName;
|
|
||||||
QStringList fileTypes = SessionFileFormat::supportedFileTypes(
|
QStringList fileTypes = SessionFileFormat::supportedFileTypes(
|
||||||
SessionFileFormat::kOpenFile);
|
SessionFileFormat::kOpenFile);
|
||||||
QString fileType;
|
QString fileType;
|
||||||
QString errorStr;
|
QString errorStr;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
|
if (!fileName.isEmpty())
|
||||||
|
goto _skip_prompt;
|
||||||
|
|
||||||
if (portsWindow->portGroupCount()) {
|
if (portsWindow->portGroupCount()) {
|
||||||
if (QMessageBox::question(this,
|
if (QMessageBox::question(this,
|
||||||
tr("Open Session"),
|
tr("Open Session"),
|
||||||
@ -177,6 +214,7 @@ void MainWindow::on_actionOpenSession_triggered()
|
|||||||
if (fileName.isEmpty())
|
if (fileName.isEmpty())
|
||||||
goto _exit;
|
goto _exit;
|
||||||
|
|
||||||
|
_skip_prompt:
|
||||||
ret = openSession(fileName, errorStr);
|
ret = openSession(fileName, errorStr);
|
||||||
if (!ret || !errorStr.isEmpty()) {
|
if (!ret || !errorStr.isEmpty()) {
|
||||||
QMessageBox msgBox(this);
|
QMessageBox msgBox(this);
|
||||||
@ -293,7 +331,7 @@ void MainWindow::on_actionViewRestoreDefaults_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionHelpOnline_triggered()
|
void MainWindow::on_actionHelpOnline_triggered()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(QUrl("http://ostinato.org/docs"));
|
QDesktopServices::openUrl(QUrl(jumpUrl("help", "app", "menu")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionHelpAbout_triggered()
|
void MainWindow::on_actionHelpAbout_triggered()
|
||||||
@ -310,10 +348,76 @@ void MainWindow::on_actionHelpAbout_triggered()
|
|||||||
delete aboutDialog;
|
delete aboutDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::stopLocalServerMonitor()
|
||||||
|
{
|
||||||
|
// We are only interested in startup errors
|
||||||
|
disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||||
|
this, SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||||
|
disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||||
|
this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLocalServerFinished(int exitCode,
|
||||||
|
QProcess::ExitStatus /*exitStatus*/)
|
||||||
|
{
|
||||||
|
if (exitCode)
|
||||||
|
reportLocalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLocalServerError(QProcess::ProcessError /*error*/)
|
||||||
|
{
|
||||||
|
reportLocalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::reportLocalServerError()
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(this);
|
||||||
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
|
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5"); // mouse copy
|
||||||
|
QString errorStr = tr("<p>Failed to start the local drone agent - "
|
||||||
|
"error 0x%1, exit status 0x%2 exit code 0x%3.</p>")
|
||||||
|
.arg(localServer_->error(), 0, 16)
|
||||||
|
.arg(localServer_->exitStatus(), 0, 16)
|
||||||
|
.arg(localServer_->exitCode(), 0, 16);
|
||||||
|
if (localServer_->error() == QProcess::FailedToStart)
|
||||||
|
errorStr.append(tr("<p>The drone program does not exist at %1 or you "
|
||||||
|
"don't have sufficient permissions to execute it."
|
||||||
|
"</p>")
|
||||||
|
.arg(QCoreApplication::applicationDirPath()));
|
||||||
|
if (localServer_->exitCode() == 1)
|
||||||
|
errorStr.append(tr("<p>The drone program was not able to bind to "
|
||||||
|
"TCP port 7878 - maybe a drone process is already "
|
||||||
|
"running?</p>"));
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND)
|
||||||
|
errorStr.append(tr("<p>This is most likely because Packet.dll "
|
||||||
|
"was not found - make sure you have "
|
||||||
|
"<a href='%1'>WinPcap"
|
||||||
|
"</a> installed.</p>")
|
||||||
|
.arg(jumpUrl("winpcap")));
|
||||||
|
#endif
|
||||||
|
msgBox.setText(errorStr);
|
||||||
|
msgBox.setInformativeText(tr("Try running drone directly."));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QMessageBox::information(this, QString(),
|
||||||
|
tr("<p>If you have remote drone agents running, you can still add "
|
||||||
|
"and connect to them.</p>"
|
||||||
|
"<p>If you don't want to start the local drone agent at startup, "
|
||||||
|
"provide the <b>-s</b> option to Ostinato on the command line.</p>"
|
||||||
|
"<p>Learn about Ostinato's <a href='%1'>Controller-Agent "
|
||||||
|
"architecture</a></p>").arg(jumpUrl("arch")));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onNewVersion(QString newVersion)
|
void MainWindow::onNewVersion(QString newVersion)
|
||||||
{
|
{
|
||||||
statusBar()->showMessage(QString("New Ostinato version %1 available. "
|
QLabel *msg = new QLabel(tr("New Ostinato version %1 available. Visit "
|
||||||
"Visit http://ostinato.org to download").arg(newVersion));
|
"<a href='%2'>ostinato.org</a> to download")
|
||||||
|
.arg(newVersion)
|
||||||
|
.arg(jumpUrl("download", "app", "status", "update")));
|
||||||
|
msg->setOpenExternalLinks(true);
|
||||||
|
statusBar()->addPermanentWidget(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Returns true on success (or user cancel) and false on failure
|
//! Returns true on success (or user cancel) and false on failure
|
||||||
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
class PortsWindow;
|
class PortsWindow;
|
||||||
class PortStatsWindow;
|
class PortStatsWindow;
|
||||||
@ -51,7 +52,7 @@ public:
|
|||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void on_actionOpenSession_triggered();
|
void on_actionOpenSession_triggered(QString fileName = QString());
|
||||||
void on_actionSaveSession_triggered();
|
void on_actionSaveSession_triggered();
|
||||||
void on_actionPreferences_triggered();
|
void on_actionPreferences_triggered();
|
||||||
void on_actionViewRestoreDefaults_triggered();
|
void on_actionViewRestoreDefaults_triggered();
|
||||||
@ -59,6 +60,10 @@ public slots:
|
|||||||
void on_actionHelpAbout_triggered();
|
void on_actionHelpAbout_triggered();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void stopLocalServerMonitor();
|
||||||
|
void onLocalServerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
void onLocalServerError(QProcess::ProcessError error);
|
||||||
|
void reportLocalServerError();
|
||||||
void onNewVersion(QString version);
|
void onNewVersion(QString version);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,6 +104,9 @@
|
|||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionHelpOnline" >
|
<action name="actionHelpOnline" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/help.png</iconset>
|
||||||
|
</property>
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Help (Online)</string>
|
<string>Help (Online)</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -90,6 +90,7 @@ SOURCES += \
|
|||||||
mainwindow.cpp \
|
mainwindow.cpp \
|
||||||
ndpstatusmodel.cpp \
|
ndpstatusmodel.cpp \
|
||||||
packetmodel.cpp \
|
packetmodel.cpp \
|
||||||
|
params.cpp \
|
||||||
port.cpp \
|
port.cpp \
|
||||||
portconfigdialog.cpp \
|
portconfigdialog.cpp \
|
||||||
portgroup.cpp \
|
portgroup.cpp \
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<file>icons/devicegroup_edit.png</file>
|
<file>icons/devicegroup_edit.png</file>
|
||||||
<file>icons/exit.png</file>
|
<file>icons/exit.png</file>
|
||||||
<file>icons/gaps.png</file>
|
<file>icons/gaps.png</file>
|
||||||
|
<file>icons/help.png</file>
|
||||||
<file>icons/logo.png</file>
|
<file>icons/logo.png</file>
|
||||||
<file>icons/magnifier.png</file>
|
<file>icons/magnifier.png</file>
|
||||||
<file>icons/name.png</file>
|
<file>icons/name.png</file>
|
||||||
|
65
client/params.cpp
Normal file
65
client/params.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2016 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 "params.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
Params::Params()
|
||||||
|
{
|
||||||
|
localDrone_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Params::parseCommandLine(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int c, n = 0;
|
||||||
|
|
||||||
|
opterr = 0;
|
||||||
|
while ((c = getopt (argc, argv, "s")) != -1) {
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
localDrone_ = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug("ignoring unrecognized option (%c)", c);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = optind; i < argc; i++, n++)
|
||||||
|
args_ << argv[i];
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Params::optLocalDrone()
|
||||||
|
{
|
||||||
|
return localDrone_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Params::argumentCount()
|
||||||
|
{
|
||||||
|
return args_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Params::argument(int index)
|
||||||
|
{
|
||||||
|
return index < args_.size() ? args_.at(index) : QString();
|
||||||
|
}
|
43
client/params.h
Normal file
43
client/params.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2016 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 _PARAMS_H
|
||||||
|
#define _PARAMS_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class Params {
|
||||||
|
public:
|
||||||
|
Params();
|
||||||
|
int parseCommandLine(int argc, char* argv[]);
|
||||||
|
|
||||||
|
bool optLocalDrone();
|
||||||
|
|
||||||
|
int argumentCount();
|
||||||
|
QString argument(int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool localDrone_;
|
||||||
|
QStringList args_;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Params appParams;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -57,6 +57,7 @@ Port::Port(quint32 id, quint32 portGroupId)
|
|||||||
stats.mutable_port_id()->set_id(id);
|
stats.mutable_port_id()->set_id(id);
|
||||||
mPortGroupId = portGroupId;
|
mPortGroupId = portGroupId;
|
||||||
capFile_ = NULL;
|
capFile_ = NULL;
|
||||||
|
dirty_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Port::~Port()
|
Port::~Port()
|
||||||
@ -100,6 +101,15 @@ void Port::reorderStreamsByOrdinals()
|
|||||||
qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan);
|
qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Port::setDirty(bool dirty)
|
||||||
|
{
|
||||||
|
if (dirty == dirty_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dirty_ = dirty;
|
||||||
|
emit localConfigChanged(mPortGroupId, mPortId, dirty_);
|
||||||
|
}
|
||||||
|
|
||||||
void Port::recalculateAverageRates()
|
void Port::recalculateAverageRates()
|
||||||
{
|
{
|
||||||
double pps = 0;
|
double pps = 0;
|
||||||
@ -209,6 +219,7 @@ void Port::setAveragePacketRate(double packetsPerSec)
|
|||||||
Q_ASSERT(false); // Unreachable!!
|
Q_ASSERT(false); // Unreachable!!
|
||||||
}
|
}
|
||||||
numActiveStreams_ = n;
|
numActiveStreams_ = n;
|
||||||
|
setDirty(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||||
@ -282,6 +293,7 @@ void Port::setAverageBitRate(double bitsPerSec)
|
|||||||
Q_ASSERT(false); // Unreachable!!
|
Q_ASSERT(false); // Unreachable!!
|
||||||
}
|
}
|
||||||
numActiveStreams_ = n;
|
numActiveStreams_ = n;
|
||||||
|
setDirty(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||||
@ -305,6 +317,7 @@ bool Port::newStreamAt(int index, OstProto::Stream const *stream)
|
|||||||
mStreams.insert(index, s);
|
mStreams.insert(index, s);
|
||||||
updateStreamOrdinalsFromIndex();
|
updateStreamOrdinalsFromIndex();
|
||||||
recalculateAverageRates();
|
recalculateAverageRates();
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -317,6 +330,7 @@ bool Port::deleteStreamAt(int index)
|
|||||||
delete mStreams.takeAt(index);
|
delete mStreams.takeAt(index);
|
||||||
updateStreamOrdinalsFromIndex();
|
updateStreamOrdinalsFromIndex();
|
||||||
recalculateAverageRates();
|
recalculateAverageRates();
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -506,6 +520,8 @@ void Port::when_syncComplete()
|
|||||||
deviceGroups_.at(i)->device_group_id().id());
|
deviceGroups_.at(i)->device_group_id().id());
|
||||||
}
|
}
|
||||||
modifiedDeviceGroupList_.clear();
|
modifiedDeviceGroupList_.clear();
|
||||||
|
|
||||||
|
setDirty(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Port::updateStats(OstProto::PortStats *portStats)
|
void Port::updateStats(OstProto::PortStats *portStats)
|
||||||
@ -543,6 +559,7 @@ void Port::duplicateStreams(const QList<int> &list, int count)
|
|||||||
insertAt++;
|
insertAt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
emit streamListChanged(mPortGroupId, mPortId);
|
emit streamListChanged(mPortGroupId, mPortId);
|
||||||
}
|
}
|
||||||
@ -625,6 +642,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
|||||||
if (i % 32 == 0)
|
if (i % 32 == 0)
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
}
|
}
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
_user_cancel:
|
_user_cancel:
|
||||||
emit streamListChanged(mPortGroupId, mPortId);
|
emit streamListChanged(mPortGroupId, mPortId);
|
||||||
@ -743,11 +761,12 @@ OstProto::DeviceGroup* Port::mutableDeviceGroupByIndex(int index)
|
|||||||
|
|
||||||
// Caller can modify DeviceGroup - assume she will
|
// Caller can modify DeviceGroup - assume she will
|
||||||
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
return devGrp;
|
return devGrp;
|
||||||
}
|
}
|
||||||
|
|
||||||
OstProto::DeviceGroup* Port::deviceGroupById(uint deviceGroupId)
|
const OstProto::DeviceGroup* Port::deviceGroupById(uint deviceGroupId) const
|
||||||
{
|
{
|
||||||
for (int i = 0; i < deviceGroups_.size(); i++) {
|
for (int i = 0; i < deviceGroups_.size(); i++) {
|
||||||
OstProto::DeviceGroup *devGrp = deviceGroups_.at(i);
|
OstProto::DeviceGroup *devGrp = deviceGroups_.at(i);
|
||||||
@ -776,6 +795,7 @@ bool Port::newDeviceGroupAt(int index, const OstProto::DeviceGroup *deviceGroup)
|
|||||||
devGrp->mutable_device_group_id()->set_id(newDeviceGroupId());
|
devGrp->mutable_device_group_id()->set_id(newDeviceGroupId());
|
||||||
deviceGroups_.insert(index, devGrp);
|
deviceGroups_.insert(index, devGrp);
|
||||||
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -788,6 +808,7 @@ bool Port::deleteDeviceGroupAt(int index)
|
|||||||
OstProto::DeviceGroup *devGrp = deviceGroups_.takeAt(index);
|
OstProto::DeviceGroup *devGrp = deviceGroups_.takeAt(index);
|
||||||
modifiedDeviceGroupList_.remove(devGrp->device_group_id().id());
|
modifiedDeviceGroupList_.remove(devGrp->device_group_id().id());
|
||||||
delete devGrp;
|
delete devGrp;
|
||||||
|
setDirty(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -818,7 +839,12 @@ bool Port::updateDeviceGroup(
|
|||||||
uint deviceGroupId,
|
uint deviceGroupId,
|
||||||
OstProto::DeviceGroup *deviceGroup)
|
OstProto::DeviceGroup *deviceGroup)
|
||||||
{
|
{
|
||||||
OstProto::DeviceGroup *devGrp = deviceGroupById(deviceGroupId);
|
using OstProto::DeviceGroup;
|
||||||
|
|
||||||
|
// XXX: We should not call mutableDeviceGroupById() because that will
|
||||||
|
// implicitly set the port as dirty, so we use a const_cast hack instead
|
||||||
|
DeviceGroup *devGrp = const_cast<DeviceGroup*>(
|
||||||
|
deviceGroupById(deviceGroupId));
|
||||||
|
|
||||||
if (!devGrp) {
|
if (!devGrp) {
|
||||||
qDebug("%s: deviceGroup id %u does not exist", __FUNCTION__,
|
qDebug("%s: deviceGroup id %u does not exist", __FUNCTION__,
|
||||||
|
@ -50,6 +50,7 @@ class Port : public QObject {
|
|||||||
quint32 mPortId;
|
quint32 mPortId;
|
||||||
quint32 mPortGroupId;
|
quint32 mPortGroupId;
|
||||||
QString mUserAlias; // user defined
|
QString mUserAlias; // user defined
|
||||||
|
bool dirty_;
|
||||||
|
|
||||||
double avgPacketsPerSec_;
|
double avgPacketsPerSec_;
|
||||||
double avgBitsPerSec_;
|
double avgBitsPerSec_;
|
||||||
@ -70,6 +71,7 @@ class Port : public QObject {
|
|||||||
void updateStreamOrdinalsFromIndex();
|
void updateStreamOrdinalsFromIndex();
|
||||||
void reorderStreamsByOrdinals();
|
void reorderStreamsByOrdinals();
|
||||||
|
|
||||||
|
void setDirty(bool dirty);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum AdminStatus { AdminDisable, AdminEnable };
|
enum AdminStatus { AdminDisable, AdminEnable };
|
||||||
@ -92,17 +94,17 @@ public:
|
|||||||
{ return QString().fromStdString(d.notes()); }
|
{ return QString().fromStdString(d.notes()); }
|
||||||
const QString userName() const
|
const QString userName() const
|
||||||
{ return QString().fromStdString(d.user_name()); }
|
{ return QString().fromStdString(d.user_name()); }
|
||||||
AdminStatus adminStatus()
|
AdminStatus adminStatus() const
|
||||||
{ return (d.is_enabled()?AdminEnable:AdminDisable); }
|
{ return (d.is_enabled()?AdminEnable:AdminDisable); }
|
||||||
bool hasExclusiveControl()
|
bool hasExclusiveControl() const
|
||||||
{ return d.is_exclusive_control(); }
|
{ return d.is_exclusive_control(); }
|
||||||
OstProto::TransmitMode transmitMode()
|
OstProto::TransmitMode transmitMode() const
|
||||||
{ return d.transmit_mode(); }
|
{ return d.transmit_mode(); }
|
||||||
bool trackStreamStats()
|
bool trackStreamStats() const
|
||||||
{ return d.is_tracking_stream_stats(); }
|
{ return d.is_tracking_stream_stats(); }
|
||||||
double averagePacketRate()
|
double averagePacketRate() const
|
||||||
{ return avgPacketsPerSec_; }
|
{ return avgPacketsPerSec_; }
|
||||||
double averageBitRate()
|
double averageBitRate() const
|
||||||
{ return avgBitsPerSec_; }
|
{ return avgBitsPerSec_; }
|
||||||
|
|
||||||
//void setAdminEnable(AdminStatus status) { mAdminStatus = status; }
|
//void setAdminEnable(AdminStatus status) { mAdminStatus = status; }
|
||||||
@ -110,11 +112,22 @@ public:
|
|||||||
//void setExclusive(bool flag);
|
//void setExclusive(bool flag);
|
||||||
|
|
||||||
int numStreams() { return mStreams.size(); }
|
int numStreams() { return mStreams.size(); }
|
||||||
Stream* streamByIndex(int index)
|
const Stream* streamByIndex(int index) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(index < mStreams.size());
|
Q_ASSERT(index < mStreams.size());
|
||||||
return mStreams[index];
|
return mStreams[index];
|
||||||
}
|
}
|
||||||
|
Stream* mutableStreamByIndex(int index, bool assumeChange = true)
|
||||||
|
{
|
||||||
|
Q_ASSERT(index < mStreams.size());
|
||||||
|
if (assumeChange)
|
||||||
|
setDirty(true);
|
||||||
|
return mStreams[index];
|
||||||
|
}
|
||||||
|
void setLocalConfigChanged(bool changed)
|
||||||
|
{
|
||||||
|
setDirty(changed);
|
||||||
|
}
|
||||||
OstProto::LinkState linkState()
|
OstProto::LinkState linkState()
|
||||||
{ return stats.state().link_state(); }
|
{ return stats.state().link_state(); }
|
||||||
|
|
||||||
@ -131,6 +144,7 @@ public:
|
|||||||
|
|
||||||
void protoDataCopyInto(OstProto::Port *data);
|
void protoDataCopyInto(OstProto::Port *data);
|
||||||
|
|
||||||
|
//! Used when config received from server
|
||||||
// FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal
|
// FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal
|
||||||
void updatePortConfig(OstProto::Port *port);
|
void updatePortConfig(OstProto::Port *port);
|
||||||
|
|
||||||
@ -146,6 +160,7 @@ public:
|
|||||||
bool updateStream(uint streamId, OstProto::Stream *stream);
|
bool updateStream(uint streamId, OstProto::Stream *stream);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
bool isDirty() { return dirty_; }
|
||||||
void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||||
void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||||
void getModifiedStreamsSinceLastSync(
|
void getModifiedStreamsSinceLastSync(
|
||||||
@ -180,7 +195,7 @@ public:
|
|||||||
int numDeviceGroups() const;
|
int numDeviceGroups() const;
|
||||||
const OstProto::DeviceGroup* deviceGroupByIndex(int index) const;
|
const OstProto::DeviceGroup* deviceGroupByIndex(int index) const;
|
||||||
OstProto::DeviceGroup* mutableDeviceGroupByIndex(int index);
|
OstProto::DeviceGroup* mutableDeviceGroupByIndex(int index);
|
||||||
OstProto::DeviceGroup* deviceGroupById(uint deviceGroupId);
|
const OstProto::DeviceGroup* deviceGroupById(uint deviceGroupId) const;
|
||||||
|
|
||||||
//! Used by StreamModel
|
//! Used by StreamModel
|
||||||
//@{
|
//@{
|
||||||
@ -218,11 +233,20 @@ public:
|
|||||||
void deviceInfoRefreshed();
|
void deviceInfoRefreshed();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
//! Used when local config changed and when config received from server
|
||||||
void portRateChanged(int portGroupId, int portId);
|
void portRateChanged(int portGroupId, int portId);
|
||||||
void portDataChanged(int portGroupId, int portId);
|
|
||||||
void streamListChanged(int portGroupId, int portId);
|
|
||||||
void deviceInfoChanged();
|
|
||||||
|
|
||||||
|
//! Used by MyService::Stub to update from config received from server
|
||||||
|
//@{
|
||||||
|
void portDataChanged(int portGroupId, int portId);
|
||||||
|
void deviceInfoChanged();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! Used when local config changed
|
||||||
|
//@{
|
||||||
|
void streamListChanged(int portGroupId, int portId);
|
||||||
|
void localConfigChanged(int portGroupId, int portId, bool changed);
|
||||||
|
//@}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "portgroup.h"
|
#include "portgroup.h"
|
||||||
|
|
||||||
|
#include "jumpurl.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include "emulproto.pb.h"
|
#include "emulproto.pb.h"
|
||||||
@ -200,6 +201,19 @@ void PortGroup::processVersionCompatibility(PbRpcController *controller)
|
|||||||
qPrintable(QString::fromStdString(verCompat->notes())));
|
qPrintable(QString::fromStdString(verCompat->notes())));
|
||||||
compat = kIncompatible;
|
compat = kIncompatible;
|
||||||
emit portGroupDataChanged(mPortGroupId);
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
|
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5");
|
||||||
|
msgBox.setText(tr("The Drone agent at %1:%2 is incompatible with this "
|
||||||
|
"Ostinato version - %3")
|
||||||
|
.arg(serverName())
|
||||||
|
.arg(int(serverPort()))
|
||||||
|
.arg(version));
|
||||||
|
msgBox.setInformativeText(QString::fromStdString(verCompat->notes()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
goto _error_exit;
|
goto _error_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,23 +316,22 @@ void PortGroup::on_rpcChannel_notification(int notifType,
|
|||||||
|
|
||||||
void PortGroup::when_portListChanged(quint32 /*portGroupId*/)
|
void PortGroup::when_portListChanged(quint32 /*portGroupId*/)
|
||||||
{
|
{
|
||||||
QString faq("http://ostinato.org/docs/faq#q-port-group-has-no-interfaces");
|
|
||||||
if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0)
|
if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0)
|
||||||
{
|
{
|
||||||
if (QMessageBox::warning(NULL, tr("No ports in portgroup"),
|
QMessageBox msgBox;
|
||||||
QString("The portgroup %1:%2 does not contain any ports!\n\n"
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
"Packet Transmit/Capture requires elevated privileges. "
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
"Please ensure that you are running 'drone' - the server "
|
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5");
|
||||||
"component of Ostinato with admin/root OR setuid privilege.\n\n"
|
QString msg = tr("<p>The portgroup %1:%2 does not contain any ports!<p>"
|
||||||
"For help see the Ostinato FAQ (%3)")
|
"<p>Packet Transmit/Capture requires special privileges. "
|
||||||
|
"Please ensure that you are running 'drone' - the agent "
|
||||||
|
"component of Ostinato with required privileges.<p>")
|
||||||
.arg(serverName())
|
.arg(serverName())
|
||||||
.arg(int(serverPort()))
|
.arg(int(serverPort()));
|
||||||
.arg(faq.remove(QRegExp("#.*$"))),
|
msgBox.setText(msg);
|
||||||
QMessageBox::Ok | QMessageBox::Help,
|
msgBox.setInformativeText(tr("See the <a href='%1'>Ostinato FAQ</a> "
|
||||||
QMessageBox::Ok) == QMessageBox::Help)
|
"for instructions to fix this problem").arg(jumpUrl("noports")));
|
||||||
{
|
msgBox.exec();
|
||||||
QDesktopServices::openUrl(QUrl(faq));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,6 +360,8 @@ void PortGroup::processPortIdList(PbRpcController *controller)
|
|||||||
p = new Port(portIdList->port_id(i).id(), mPortGroupId);
|
p = new Port(portIdList->port_id(i).id(), mPortGroupId);
|
||||||
connect(p, SIGNAL(portDataChanged(int, int)),
|
connect(p, SIGNAL(portDataChanged(int, int)),
|
||||||
this, SIGNAL(portGroupDataChanged(int, int)));
|
this, SIGNAL(portGroupDataChanged(int, int)));
|
||||||
|
connect(p, SIGNAL(localConfigChanged(int, int, bool)),
|
||||||
|
this, SIGNAL(portGroupDataChanged(int, int)));
|
||||||
qDebug("before port append\n");
|
qDebug("before port append\n");
|
||||||
mPorts.append(p);
|
mPorts.append(p);
|
||||||
atConnectPortConfig_.append(NULL); // will be filled later
|
atConnectPortConfig_.append(NULL); // will be filled later
|
||||||
|
@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "portgrouplist.h"
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
// TODO(LOW): Remove
|
// TODO(LOW): Remove
|
||||||
#include <modeltest.h>
|
#include <modeltest.h>
|
||||||
|
|
||||||
@ -29,8 +31,6 @@ PortGroupList::PortGroupList()
|
|||||||
mDeviceGroupModel(this),
|
mDeviceGroupModel(this),
|
||||||
mDeviceModel(this)
|
mDeviceModel(this)
|
||||||
{
|
{
|
||||||
PortGroup *pg;
|
|
||||||
|
|
||||||
#ifdef QT_NO_DEBUG
|
#ifdef QT_NO_DEBUG
|
||||||
streamModelTester_ = NULL;
|
streamModelTester_ = NULL;
|
||||||
portModelTester_ = NULL;
|
portModelTester_ = NULL;
|
||||||
@ -46,9 +46,11 @@ PortGroupList::PortGroupList()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add the "Local" Port Group
|
// Add the "Local" Port Group
|
||||||
pg = new PortGroup;
|
if (appParams.optLocalDrone()) {
|
||||||
|
PortGroup *pg = new PortGroup;
|
||||||
addPortGroup(*pg);
|
addPortGroup(*pg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PortGroupList::~PortGroupList()
|
PortGroupList::~PortGroupList()
|
||||||
{
|
{
|
||||||
|
@ -187,6 +187,10 @@ QVariant PortModel::data(const QModelIndex &index, int role) const
|
|||||||
{
|
{
|
||||||
return portIconFactory[port->linkState()][port->hasExclusiveControl()];
|
return portIconFactory[port->linkState()][port->hasExclusiveControl()];
|
||||||
}
|
}
|
||||||
|
else if ((role == Qt::ForegroundRole))
|
||||||
|
{
|
||||||
|
return port->isDirty() ? QBrush(Qt::red) : QVariant();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBG0("Exit PortModel data 6\n");
|
DBG0("Exit PortModel data 6\n");
|
||||||
|
@ -53,6 +53,12 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
|||||||
tvPortStats->verticalHeader()->setDefaultSectionSize(
|
tvPortStats->verticalHeader()->setDefaultSectionSize(
|
||||||
tvPortStats->verticalHeader()->minimumSectionSize());
|
tvPortStats->verticalHeader()->minimumSectionSize());
|
||||||
|
|
||||||
|
connect(tvPortStats->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(
|
||||||
|
const QItemSelection&, const QItemSelection&)),
|
||||||
|
SLOT(when_tvPortStats_selectionChanged(
|
||||||
|
const QItemSelection&, const QItemSelection&)));
|
||||||
|
when_tvPortStats_selectionChanged(QItemSelection(), QItemSelection());
|
||||||
}
|
}
|
||||||
|
|
||||||
PortStatsWindow::~PortStatsWindow()
|
PortStatsWindow::~PortStatsWindow()
|
||||||
@ -76,12 +82,43 @@ void PortStatsWindow::showMyReservedPortsOnly(bool enabled)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ------------- SLOTS (private) -------------- */
|
/* ------------- SLOTS (private) -------------- */
|
||||||
|
|
||||||
|
void PortStatsWindow::when_tvPortStats_selectionChanged(
|
||||||
|
const QItemSelection& /*selected*/,
|
||||||
|
const QItemSelection& /*deselected*/)
|
||||||
|
{
|
||||||
|
QModelIndexList indexList =
|
||||||
|
tvPortStats->selectionModel()->selectedColumns();
|
||||||
|
|
||||||
|
if (proxyStatsModel) {
|
||||||
|
selectedColumns.clear();
|
||||||
|
foreach(QModelIndex index, indexList)
|
||||||
|
selectedColumns.append(proxyStatsModel->mapToSource(index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
selectedColumns = indexList;
|
||||||
|
|
||||||
|
bool isEmpty = selectedColumns.isEmpty();
|
||||||
|
|
||||||
|
tbStartTransmit->setDisabled(isEmpty);
|
||||||
|
tbStopTransmit->setDisabled(isEmpty);
|
||||||
|
|
||||||
|
tbStartCapture->setDisabled(isEmpty);
|
||||||
|
tbStopCapture->setDisabled(isEmpty);
|
||||||
|
tbViewCapture->setDisabled(isEmpty);
|
||||||
|
|
||||||
|
tbClear->setDisabled(isEmpty);
|
||||||
|
|
||||||
|
tbResolveNeighbors->setDisabled(isEmpty);
|
||||||
|
tbClearNeighbors->setDisabled(isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
void PortStatsWindow::on_tbStartTransmit_clicked()
|
void PortStatsWindow::on_tbStartTransmit_clicked()
|
||||||
{
|
{
|
||||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), pgpl);
|
model->portListFromIndex(selectedColumns, pgpl);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < pgpl.size(); i++)
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
@ -96,7 +133,7 @@ void PortStatsWindow::on_tbStopTransmit_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), pgpl);
|
model->portListFromIndex(selectedColumns, pgpl);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < pgpl.size(); i++)
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
@ -112,7 +149,7 @@ void PortStatsWindow::on_tbStartCapture_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), pgpl);
|
model->portListFromIndex(selectedColumns, pgpl);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < pgpl.size(); i++)
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
@ -128,7 +165,7 @@ void PortStatsWindow::on_tbStopCapture_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), pgpl);
|
model->portListFromIndex(selectedColumns, pgpl);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < pgpl.size(); i++)
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
@ -144,7 +181,7 @@ void PortStatsWindow::on_tbViewCapture_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), pgpl);
|
model->portListFromIndex(selectedColumns, pgpl);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < pgpl.size(); i++)
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
@ -159,7 +196,7 @@ void PortStatsWindow::on_tbResolveNeighbors_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), portList);
|
model->portListFromIndex(selectedColumns, portList);
|
||||||
|
|
||||||
// Resolve ARP/ND for selected ports, portgroup by portgroup
|
// Resolve ARP/ND for selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < portList.size(); i++)
|
for (int i = 0; i < portList.size(); i++)
|
||||||
@ -174,7 +211,7 @@ void PortStatsWindow::on_tbClearNeighbors_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), portList);
|
model->portListFromIndex(selectedColumns, portList);
|
||||||
|
|
||||||
// Clear ARP/ND for ports, portgroup by portgroup
|
// Clear ARP/ND for ports, portgroup by portgroup
|
||||||
for (int i = 0; i < portList.size(); i++)
|
for (int i = 0; i < portList.size(); i++)
|
||||||
@ -189,7 +226,7 @@ void PortStatsWindow::on_tbClear_clicked()
|
|||||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), portList);
|
model->portListFromIndex(selectedColumns, portList);
|
||||||
|
|
||||||
// Clear selected ports, portgroup by portgroup
|
// Clear selected ports, portgroup by portgroup
|
||||||
for (int i = 0; i < portList.size(); i++)
|
for (int i = 0; i < portList.size(); i++)
|
||||||
@ -235,7 +272,7 @@ void PortStatsWindow::on_tbGetStreamStats_clicked()
|
|||||||
StreamStatsModel *streamStatsModel;
|
StreamStatsModel *streamStatsModel;
|
||||||
|
|
||||||
// Get selected ports
|
// Get selected ports
|
||||||
model->portListFromIndex(selectedColumns(), portList);
|
model->portListFromIndex(selectedColumns, portList);
|
||||||
|
|
||||||
if (portList.size()) {
|
if (portList.size()) {
|
||||||
QDockWidget *dock = new QDockWidget(mainWindow);
|
QDockWidget *dock = new QDockWidget(mainWindow);
|
||||||
@ -298,20 +335,3 @@ void PortStatsWindow::on_tbFilter_clicked()
|
|||||||
hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi);
|
hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------ Private Methods -------------- */
|
|
||||||
|
|
||||||
QModelIndexList PortStatsWindow::selectedColumns()
|
|
||||||
{
|
|
||||||
QModelIndexList indexList =
|
|
||||||
tvPortStats->selectionModel()->selectedColumns();
|
|
||||||
QModelIndexList sourceIndexList;
|
|
||||||
|
|
||||||
if (!proxyStatsModel)
|
|
||||||
return indexList;
|
|
||||||
|
|
||||||
foreach(QModelIndex index, indexList)
|
|
||||||
sourceIndexList.append(proxyStatsModel->mapToSource(index));
|
|
||||||
|
|
||||||
return sourceIndexList;
|
|
||||||
}
|
|
||||||
|
@ -40,6 +40,9 @@ public slots:
|
|||||||
void showMyReservedPortsOnly(bool enabled);
|
void showMyReservedPortsOnly(bool enabled);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void when_tvPortStats_selectionChanged(const QItemSelection &selected,
|
||||||
|
const QItemSelection &deselected);
|
||||||
|
|
||||||
void on_tbStartTransmit_clicked();
|
void on_tbStartTransmit_clicked();
|
||||||
void on_tbStopTransmit_clicked();
|
void on_tbStopTransmit_clicked();
|
||||||
|
|
||||||
@ -57,11 +60,10 @@ private slots:
|
|||||||
void on_tbFilter_clicked();
|
void on_tbFilter_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QModelIndexList selectedColumns();
|
|
||||||
|
|
||||||
PortGroupList *pgl;
|
PortGroupList *pgl;
|
||||||
PortStatsModel *model;
|
PortStatsModel *model;
|
||||||
QSortFilterProxyModel *proxyStatsModel;
|
QSortFilterProxyModel *proxyStatsModel;
|
||||||
|
QModelIndexList selectedColumns;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>PortStatsWindow</class>
|
<class>PortStatsWindow</class>
|
||||||
<widget class="QWidget" name="PortStatsWindow">
|
<widget class="QWidget" name="PortStatsWindow">
|
||||||
@ -22,6 +23,13 @@
|
|||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Transmit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="tbStartTransmit">
|
<widget class="QToolButton" name="tbStartTransmit">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@ -31,10 +39,11 @@
|
|||||||
<string>Starts transmit on selected port(s)</string>
|
<string>Starts transmit on selected port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Start Transmit</string>
|
<string>Start</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/control_play.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/control_play.png</normaloff>:/icons/control_play.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -47,10 +56,25 @@
|
|||||||
<string>Stops transmit on selected port(s)</string>
|
<string>Stops transmit on selected port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Stop Trasmit</string>
|
<string>Stop</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/control_stop.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/control_stop.png</normaloff>:/icons/control_stop.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stats</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -66,7 +90,8 @@
|
|||||||
<string>Clear</string>
|
<string>Clear</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portstats_clear.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portstats_clear.png</normaloff>:/icons/portstats_clear.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -82,7 +107,8 @@
|
|||||||
<string>Clear All</string>
|
<string>Clear All</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portstats_clear_all.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portstats_clear_all.png</normaloff>:/icons/portstats_clear_all.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -102,6 +128,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Capture</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="tbStartCapture" >
|
<widget class="QToolButton" name="tbStartCapture" >
|
||||||
<property name="toolTip" >
|
<property name="toolTip" >
|
||||||
@ -111,10 +151,11 @@
|
|||||||
<string>Captures packets on the selected port(s)</string>
|
<string>Captures packets on the selected port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Start Capture</string>
|
<string>Start</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/sound_none.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/sound_none.png</normaloff>:/icons/sound_none.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -127,10 +168,11 @@
|
|||||||
<string>End capture on selecteed port(s)</string>
|
<string>End capture on selecteed port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Stop Capture</string>
|
<string>Stop</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/sound_mute.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/sound_mute.png</normaloff>:/icons/sound_mute.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -143,10 +185,11 @@
|
|||||||
<string>View captured packets on selected port(s)</string>
|
<string>View captured packets on selected port(s)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>View Capture</string>
|
<string>View</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/magnifier.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/magnifier.png</normaloff>:/icons/magnifier.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -157,6 +200,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>ARP/ND</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="tbResolveNeighbors">
|
<widget class="QToolButton" name="tbResolveNeighbors">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@ -169,7 +219,8 @@
|
|||||||
<string>Resolve Neighbors</string>
|
<string>Resolve Neighbors</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/neighbor_resolve.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/neighbor_resolve.png</normaloff>:/icons/neighbor_resolve.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -185,7 +236,8 @@
|
|||||||
<string>Clear Neighbors</string>
|
<string>Clear Neighbors</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/neighbor_clear.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/neighbor_clear.png</normaloff>:/icons/neighbor_clear.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -201,7 +253,7 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>40</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
@ -218,7 +270,8 @@
|
|||||||
<string>Filter</string>
|
<string>Filter</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portstats_filter.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portstats_filter.png</normaloff>:/icons/portstats_filter.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -226,7 +279,11 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="tvPortStats" />
|
<widget class="QTableView" name="tvPortStats">
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectColumns</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -302,26 +302,26 @@ void PortsWindow::showMyReservedPortsOnly(bool enabled)
|
|||||||
|
|
||||||
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
|
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
|
||||||
{
|
{
|
||||||
QModelIndex currentPort = tvPortList->currentIndex();
|
|
||||||
StreamConfigDialog *scd;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (proxyPortModel)
|
|
||||||
currentPort = proxyPortModel->mapToSource(currentPort);
|
|
||||||
|
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
{
|
{
|
||||||
qDebug("%s: invalid index", __FUNCTION__);
|
qDebug("%s: invalid index", __FUNCTION__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scd = new StreamConfigDialog(plm->port(currentPort), index.row(), this);
|
|
||||||
qDebug("stream list activated\n");
|
qDebug("stream list activated\n");
|
||||||
ret = scd->exec();
|
|
||||||
|
|
||||||
if (ret == QDialog::Accepted)
|
Port &curPort = plm->port(proxyPortModel ?
|
||||||
plm->port(currentPort).recalculateAverageRates();
|
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||||
|
tvPortList->currentIndex());
|
||||||
|
|
||||||
delete scd;
|
QList<Stream*> streams;
|
||||||
|
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
if (scd.exec() == QDialog::Accepted) {
|
||||||
|
curPort.recalculateAverageRates();
|
||||||
|
curPort.setLocalConfigChanged(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||||
@ -345,12 +345,16 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
|||||||
{
|
{
|
||||||
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
|
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
|
||||||
this, SLOT(updatePortRates()));
|
this, SLOT(updatePortRates()));
|
||||||
|
disconnect(&(plm->port(previous)),
|
||||||
|
SIGNAL(localConfigChanged(int, int, bool)),
|
||||||
|
this,
|
||||||
|
SLOT(updateApplyHint(int, int, bool)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
{
|
{
|
||||||
qDebug("setting stacked widget to blank page");
|
qDebug("setting stacked widget to welcome page");
|
||||||
swDetail->setCurrentIndex(2); // blank page
|
swDetail->setCurrentIndex(0); // welcome page
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -360,10 +364,21 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
|||||||
}
|
}
|
||||||
else if (plm->isPort(current))
|
else if (plm->isPort(current))
|
||||||
{
|
{
|
||||||
swDetail->setCurrentIndex(0); // port detail page
|
swDetail->setCurrentIndex(2); // port detail page
|
||||||
updatePortRates();
|
updatePortRates();
|
||||||
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
|
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
|
||||||
SLOT(updatePortRates()));
|
SLOT(updatePortRates()));
|
||||||
|
connect(&(plm->port(current)),
|
||||||
|
SIGNAL(localConfigChanged(int, int, bool)),
|
||||||
|
SLOT(updateApplyHint(int, int, bool)));
|
||||||
|
if (plm->port(current).isDirty())
|
||||||
|
updateApplyHint(plm->port(current).portGroupId(),
|
||||||
|
plm->port(current).id(), true);
|
||||||
|
else if (plm->port(current).numStreams())
|
||||||
|
applyHint->setText("Use the Statistics window to transmit "
|
||||||
|
"packets");
|
||||||
|
else
|
||||||
|
applyHint->setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +388,22 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
|||||||
void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft,
|
void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft,
|
||||||
const QModelIndex& bottomRight)
|
const QModelIndex& bottomRight)
|
||||||
{
|
{
|
||||||
qDebug("In %s", __FUNCTION__);
|
qDebug("In %s %d:(%d, %d) - %d:(%d, %d)", __FUNCTION__,
|
||||||
|
topLeft.parent().isValid(), topLeft.row(), topLeft.column(),
|
||||||
|
bottomRight.parent().isValid(), bottomRight.row(), bottomRight.column());
|
||||||
|
|
||||||
|
if (!topLeft.isValid() || !bottomRight.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (topLeft.parent() != bottomRight.parent())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If a port has changed, expand the port group
|
||||||
|
if (topLeft.parent().isValid())
|
||||||
|
tvPortList->expand(proxyPortModel ?
|
||||||
|
proxyPortModel->mapFromSource(topLeft.parent()) :
|
||||||
|
topLeft.parent());
|
||||||
|
|
||||||
#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex
|
#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex
|
||||||
if ((tvPortList->currentIndex() >= topLeft) &&
|
if ((tvPortList->currentIndex() >= topLeft) &&
|
||||||
(tvPortList->currentIndex() <= bottomRight))
|
(tvPortList->currentIndex() <= bottomRight))
|
||||||
@ -469,11 +499,6 @@ void PortsWindow::updateStreamViewActions()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
actionNew_Stream->setEnabled(true);
|
actionNew_Stream->setEnabled(true);
|
||||||
|
|
||||||
// Enable "Edit" only if the single range has a single row
|
|
||||||
if (tvStreamList->selectionModel()->selection().at(0).height() > 1)
|
|
||||||
actionEdit_Stream->setDisabled(true);
|
|
||||||
else
|
|
||||||
actionEdit_Stream->setEnabled(true);
|
actionEdit_Stream->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,6 +521,18 @@ void PortsWindow::updateStreamViewActions()
|
|||||||
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
|
||||||
|
bool configChanged)
|
||||||
|
{
|
||||||
|
if (configChanged)
|
||||||
|
applyHint->setText("Configuration has changed - "
|
||||||
|
"<font color='red'><b>click Apply</b></font> "
|
||||||
|
"to activate the changes");
|
||||||
|
else
|
||||||
|
applyHint->setText("Configuration activated. Use the Statistics "
|
||||||
|
"window to transmit packets");
|
||||||
|
}
|
||||||
|
|
||||||
void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex)
|
void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex)
|
||||||
{
|
{
|
||||||
QModelIndex current = currentIndex;
|
QModelIndex current = currentIndex;
|
||||||
@ -740,30 +777,59 @@ void PortsWindow::on_actionNew_Stream_triggered()
|
|||||||
{
|
{
|
||||||
qDebug("New Stream Action");
|
qDebug("New Stream Action");
|
||||||
|
|
||||||
// In case nothing is selected, insert 1 row at the top
|
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||||
int row = 0, count = 1;
|
if (selectionModel->selection().size() > 1) {
|
||||||
|
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
|
||||||
|
selectionModel->selection().size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case nothing is selected, insert 1 row at the end
|
||||||
|
StreamModel *streamModel = plm->getStreamModel();
|
||||||
|
int row = streamModel->rowCount(), count = 1;
|
||||||
|
|
||||||
// In case we have a single range selected; insert as many rows as
|
// In case we have a single range selected; insert as many rows as
|
||||||
// in the singe selected range before the top of the selected range
|
// in the singe selected range before the top of the selected range
|
||||||
if (tvStreamList->selectionModel()->selection().size() == 1)
|
if (selectionModel->selection().size() == 1)
|
||||||
{
|
{
|
||||||
row = tvStreamList->selectionModel()->selection().at(0).top();
|
row = selectionModel->selection().at(0).top();
|
||||||
count = tvStreamList->selectionModel()->selection().at(0).height();
|
count = selectionModel->selection().at(0).height();
|
||||||
}
|
}
|
||||||
|
|
||||||
plm->getStreamModel()->insertRows(row, count);
|
Port &curPort = plm->port(proxyPortModel ?
|
||||||
|
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||||
|
tvPortList->currentIndex());
|
||||||
|
|
||||||
|
QList<Stream*> streams;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
streams.append(new Stream);
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
scd.setWindowTitle(tr("Add Stream"));
|
||||||
|
if (scd.exec() == QDialog::Accepted)
|
||||||
|
streamModel->insert(row, streams);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortsWindow::on_actionEdit_Stream_triggered()
|
void PortsWindow::on_actionEdit_Stream_triggered()
|
||||||
{
|
{
|
||||||
qDebug("Edit Stream Action");
|
qDebug("Edit Stream Action");
|
||||||
|
|
||||||
// Ensure we have only one range selected which contains only one row
|
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
|
||||||
if ((tvStreamList->selectionModel()->selection().size() == 1) &&
|
if (!streamModel->hasSelection())
|
||||||
(tvStreamList->selectionModel()->selection().at(0).height() == 1))
|
return;
|
||||||
{
|
|
||||||
on_tvStreamList_activated(tvStreamList->selectionModel()->
|
Port &curPort = plm->port(proxyPortModel ?
|
||||||
selection().at(0).topLeft());
|
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||||
|
tvPortList->currentIndex());
|
||||||
|
|
||||||
|
QList<Stream*> streams;
|
||||||
|
foreach(QModelIndex index, streamModel->selectedRows())
|
||||||
|
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||||
|
|
||||||
|
StreamConfigDialog scd(streams, curPort, this);
|
||||||
|
if (scd.exec() == QDialog::Accepted) {
|
||||||
|
curPort.recalculateAverageRates();
|
||||||
|
curPort.setLocalConfigChanged(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ public slots:
|
|||||||
void showMyReservedPortsOnly(bool enabled);
|
void showMyReservedPortsOnly(bool enabled);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void updateApplyHint(int portGroupId, int portId, bool configChanged);
|
||||||
void updatePortViewActions(const QModelIndex& currentIndex);
|
void updatePortViewActions(const QModelIndex& currentIndex);
|
||||||
void updateStreamViewActions();
|
void updateStreamViewActions();
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>PortsWindow</class>
|
<class>PortsWindow</class>
|
||||||
<widget class="QWidget" name="PortsWindow">
|
<widget class="QWidget" name="PortsWindow">
|
||||||
@ -21,9 +22,9 @@
|
|||||||
<property name="childrenCollapsible">
|
<property name="childrenCollapsible">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QTreeView" name="tvPortList" >
|
<widget class="XTreeView" name="tvPortList">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
<horstretch>1</horstretch>
|
<horstretch>1</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -37,26 +38,90 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QStackedWidget" name="swDetail">
|
<widget class="QStackedWidget" name="swDetail">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
<horstretch>2</horstretch>
|
<horstretch>2</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
|
<widget class="QWidget" name="blankPage">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><p><b>Welcome to Ostinato</b></p>
|
||||||
|
<p>The port list on the left contains all the ports on which you can transmit packets.</p>
|
||||||
|
<p>Ports belong to a port group. Make sure the Port Group has a <img src=":/icons/bullet_green.png"/> next to it, then double click the port group to show or hide the ports in the port group.</p>
|
||||||
|
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
|
||||||
|
<p>To create a stream, select the port on which you want to send packets.</p>
|
||||||
|
<hr/>
|
||||||
|
<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p></string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="portGroupDetail">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>You have selected a port group in the port list on the left.</p>
|
||||||
|
<p>You can transmit packets on any of the ports within the port group.</p>
|
||||||
|
<p>Make sure the port group has a <img src=":/icons/bullet_green.png"/> next to it and then double click the port group to show or hide the ports in the port group.</p>
|
||||||
|
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
|
||||||
|
<p>To create a stream, select the port on which you want to send packets. </p></string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>177</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="portDetail">
|
<widget class="QWidget" name="portDetail">
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<property name="leftMargin" >
|
<property name="margin">
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin" >
|
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -68,16 +133,7 @@
|
|||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
<property name="leftMargin" >
|
<property name="margin">
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin" >
|
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin" >
|
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin" >
|
|
||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -85,7 +141,27 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="applyHint">
|
||||||
|
<property name="text">
|
||||||
|
<string>Apply Hint</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>40</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
@ -147,7 +223,7 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>40</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
@ -158,9 +234,9 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="tvStreamList" >
|
<widget class="XTableView" name="tvStreamList">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>1</verstretch>
|
<verstretch>1</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -168,6 +244,13 @@
|
|||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::ActionsContextMenu</enum>
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>This is the stream list for the selected port
|
||||||
|
|
||||||
|
A stream is a sequence of one or more packets
|
||||||
|
|
||||||
|
Right-click to create a stream</string>
|
||||||
|
</property>
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<enum>QFrame::StyledPanel</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -190,7 +273,7 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="DevicesWidget" native="1" name="devicesWidget" />
|
<widget class="DevicesWidget" name="devicesWidget" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -198,28 +281,14 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="portGroupDetail" >
|
|
||||||
<layout class="QHBoxLayout" >
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Select a port to configure streams</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="blankPage" />
|
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
<action name="actionNew_Port_Group">
|
<action name="actionNew_Port_Group">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_add.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portgroup_add.png</normaloff>:/icons/portgroup_add.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>New Port Group</string>
|
<string>New Port Group</string>
|
||||||
@ -227,7 +296,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDelete_Port_Group">
|
<action name="actionDelete_Port_Group">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_delete.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portgroup_delete.png</normaloff>:/icons/portgroup_delete.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Delete Port Group</string>
|
<string>Delete Port Group</string>
|
||||||
@ -235,7 +305,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionConnect_Port_Group">
|
<action name="actionConnect_Port_Group">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_connect.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portgroup_connect.png</normaloff>:/icons/portgroup_connect.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Connect Port Group</string>
|
<string>Connect Port Group</string>
|
||||||
@ -243,7 +314,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDisconnect_Port_Group">
|
<action name="actionDisconnect_Port_Group">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_disconnect.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/portgroup_disconnect.png</normaloff>:/icons/portgroup_disconnect.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disconnect Port Group</string>
|
<string>Disconnect Port Group</string>
|
||||||
@ -251,7 +323,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionNew_Stream">
|
<action name="actionNew_Stream">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/stream_add.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>New Stream</string>
|
<string>New Stream</string>
|
||||||
@ -259,7 +332,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDelete_Stream">
|
<action name="actionDelete_Stream">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/stream_delete.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Delete Stream</string>
|
<string>Delete Stream</string>
|
||||||
@ -267,7 +341,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionEdit_Stream">
|
<action name="actionEdit_Stream">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/stream_edit.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Edit Stream</string>
|
<string>Edit Stream</string>
|
||||||
@ -298,7 +373,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDuplicate_Stream">
|
<action name="actionDuplicate_Stream">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/stream_duplicate.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Duplicate Stream</string>
|
<string>Duplicate Stream</string>
|
||||||
@ -312,6 +388,16 @@
|
|||||||
<header>deviceswidget.h</header>
|
<header>deviceswidget.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>XTreeView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>xtreeview.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>XTableView</class>
|
||||||
|
<extends>QTableView</extends>
|
||||||
|
<header>xtableview.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="ostinato.qrc"/>
|
<include location="ostinato.qrc"/>
|
||||||
|
@ -39,20 +39,36 @@ int StreamConfigDialog::lastProtocolDataIndex = 0;
|
|||||||
|
|
||||||
static const uint kEthFrameOverHead = 20;
|
static const uint kEthFrameOverHead = 20;
|
||||||
|
|
||||||
StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
StreamConfigDialog::StreamConfigDialog(
|
||||||
QWidget *parent) : QDialog (parent), mPort(port)
|
QList<Stream*> &streamList,
|
||||||
|
const Port &port,
|
||||||
|
QWidget *parent)
|
||||||
|
: QDialog (parent), _userStreamList(streamList), mPort(port)
|
||||||
{
|
{
|
||||||
|
mCurrentStreamIndex = 0;
|
||||||
|
|
||||||
|
Q_ASSERT(_userStreamList.size() > 0);
|
||||||
|
|
||||||
|
// Create a copy of the user provided stream list
|
||||||
|
// We need a copy because user may edit multiple streams and then
|
||||||
|
// discard the edit - in this case the user provided stream list
|
||||||
|
// should not be modified on return
|
||||||
|
foreach(Stream* stm, _userStreamList) {
|
||||||
OstProto::Stream s;
|
OstProto::Stream s;
|
||||||
mCurrentStreamIndex = streamIndex;
|
stm->protoDataCopyInto(s);
|
||||||
mpStream = new Stream;
|
_streamList.append(new Stream());
|
||||||
mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s);
|
_streamList.last()->protoDataCopyFrom(s);
|
||||||
mpStream->protoDataCopyFrom(s);
|
}
|
||||||
|
|
||||||
|
mpStream = _streamList.at(mCurrentStreamIndex);
|
||||||
_iter = mpStream->createProtocolListIterator();
|
_iter = mpStream->createProtocolListIterator();
|
||||||
isUpdateInProgress = false;
|
isUpdateInProgress = false;
|
||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
setupUiExtra();
|
setupUiExtra();
|
||||||
|
|
||||||
|
_windowTitle = windowTitle();
|
||||||
|
|
||||||
for (int i = ProtoMin; i < ProtoMax; i++)
|
for (int i = ProtoMin; i < ProtoMax; i++)
|
||||||
{
|
{
|
||||||
bgProto[i]->setProperty("ProtocolLevel", i);
|
bgProto[i]->setProperty("ProtocolLevel", i);
|
||||||
@ -154,8 +170,6 @@ 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);
|
||||||
@ -168,10 +182,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
|||||||
vwPacketDump->setModel(mpPacketModel);
|
vwPacketDump->setModel(mpPacketModel);
|
||||||
vwPacketDump->setSelectionModel(tvPacketTree->selectionModel());
|
vwPacketDump->setSelectionModel(tvPacketTree->selectionModel());
|
||||||
|
|
||||||
// TODO(MED):
|
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||||
//! \todo Enable navigation of streams
|
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||||
pbPrev->setHidden(true);
|
|
||||||
pbNext->setHidden(true);
|
|
||||||
//! \todo Support Goto Stream Id
|
//! \todo Support Goto Stream Id
|
||||||
leStreamId->setHidden(true);
|
leStreamId->setHidden(true);
|
||||||
disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool)));
|
disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool)));
|
||||||
@ -332,7 +345,14 @@ StreamConfigDialog::~StreamConfigDialog()
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete _iter;
|
delete _iter;
|
||||||
delete mpStream;
|
while (!_streamList.isEmpty())
|
||||||
|
delete _streamList.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamConfigDialog::setWindowTitle(const QString &title)
|
||||||
|
{
|
||||||
|
_windowTitle = title;
|
||||||
|
QDialog::setWindowTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamConfigDialog::loadProtocolWidgets()
|
void StreamConfigDialog::loadProtocolWidgets()
|
||||||
@ -413,30 +433,6 @@ void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamConfigDialog::on_pbPrev_clicked()
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
StoreCurrentStream(currStreamIdx);
|
|
||||||
currStreamIdx--;
|
|
||||||
LoadCurrentStream(currStreamIdx);
|
|
||||||
|
|
||||||
pbPrev->setDisabled((currStreamIdx == 0));
|
|
||||||
pbNext->setDisabled((currStreamIdx == 2));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamConfigDialog::on_pbNext_clicked()
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
StoreCurrentStream(currStreamIdx);
|
|
||||||
currStreamIdx++;
|
|
||||||
LoadCurrentStream(currStreamIdx);
|
|
||||||
|
|
||||||
pbPrev->setDisabled((currStreamIdx == 0));
|
|
||||||
pbNext->setDisabled((currStreamIdx == 2));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index)
|
void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index)
|
||||||
{
|
{
|
||||||
qDebug("%s, index = %d", __FUNCTION__, index);
|
qDebug("%s, index = %d", __FUNCTION__, index);
|
||||||
@ -990,9 +986,16 @@ void StreamConfigDialog::LoadCurrentStream()
|
|||||||
QString str;
|
QString str;
|
||||||
|
|
||||||
qDebug("loading mpStream %p", mpStream);
|
qDebug("loading mpStream %p", mpStream);
|
||||||
|
variableFieldsWidget->setStream(mpStream);
|
||||||
|
|
||||||
|
QDialog::setWindowTitle(QString("%1 [%2]").arg(_windowTitle)
|
||||||
|
.arg(mpStream->name().isEmpty() ?
|
||||||
|
tr("<unnamed>") : mpStream->name()));
|
||||||
|
|
||||||
// Meta Data
|
// Meta Data
|
||||||
{
|
{
|
||||||
|
name->setText(mpStream->name());
|
||||||
|
enabled->setChecked(mpStream->isEnabled());
|
||||||
cmbPktLenMode->setCurrentIndex(mpStream->lenMode());
|
cmbPktLenMode->setCurrentIndex(mpStream->lenMode());
|
||||||
lePktLen->setText(str.setNum(mpStream->frameLen()));
|
lePktLen->setText(str.setNum(mpStream->frameLen()));
|
||||||
lePktLenMin->setText(str.setNum(mpStream->frameLenMin()));
|
lePktLenMin->setText(str.setNum(mpStream->frameLenMin()));
|
||||||
@ -1009,6 +1012,7 @@ void StreamConfigDialog::LoadCurrentStream()
|
|||||||
|
|
||||||
// Variable Fields
|
// Variable Fields
|
||||||
{
|
{
|
||||||
|
variableFieldsWidget->clear();
|
||||||
variableFieldsWidget->load();
|
variableFieldsWidget->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1077,6 +1081,8 @@ void StreamConfigDialog::StoreCurrentStream()
|
|||||||
qDebug("storing pStream %p", pStream);
|
qDebug("storing pStream %p", pStream);
|
||||||
|
|
||||||
// Meta Data
|
// Meta Data
|
||||||
|
pStream->setName(name->text());
|
||||||
|
pStream->setEnabled(enabled->isChecked());
|
||||||
pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex());
|
pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex());
|
||||||
pStream->setFrameLen(lePktLen->text().toULong(&isOk));
|
pStream->setFrameLen(lePktLen->text().toULong(&isOk));
|
||||||
pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk));
|
pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk));
|
||||||
@ -1225,41 +1231,99 @@ void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamConfigDialog::on_pbOk_clicked()
|
bool StreamConfigDialog::isCurrentStreamValid()
|
||||||
{
|
{
|
||||||
QString log;
|
QStringList log;
|
||||||
OstProto::Stream s;
|
|
||||||
|
|
||||||
// Store dialog contents into stream
|
|
||||||
StoreCurrentStream();
|
|
||||||
|
|
||||||
if ((mPort.transmitMode() == OstProto::kInterleavedTransmit)
|
if ((mPort.transmitMode() == OstProto::kInterleavedTransmit)
|
||||||
&& (mpStream->isFrameVariable()))
|
&& (mpStream->isFrameVariable()))
|
||||||
{
|
{
|
||||||
log += "* In 'Interleaved Streams' transmit mode, the count for "
|
log << tr("In 'Interleaved Streams' transmit mode, the count for "
|
||||||
"varying fields at transmit time may not be same as configured\n";
|
"varying fields at transmit time may not be same as configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mPort.trackStreamStats()
|
if (!mPort.trackStreamStats()
|
||||||
&& mpStream->hasProtocol(OstProto::Protocol::kSignFieldNumber))
|
&& mpStream->hasProtocol(OstProto::Protocol::kSignFieldNumber))
|
||||||
{
|
{
|
||||||
log += "* Stream contains special signature, but per stream statistics "
|
log << tr("Stream contains special signature, but per stream statistics "
|
||||||
"will not be available till it is enabled on the port\n";
|
"will not be available till it is enabled on the port");
|
||||||
}
|
}
|
||||||
|
|
||||||
mpStream->preflightCheck(log);
|
mpStream->preflightCheck(log);
|
||||||
|
|
||||||
if (log.length())
|
if (log.size())
|
||||||
{
|
{
|
||||||
if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?",
|
if (QMessageBox::warning(this, "Preflight Check",
|
||||||
|
tr("<p>We found possible problems with this stream -</p>")
|
||||||
|
+ "<ul>"
|
||||||
|
+ log.replaceInStrings(QRegExp("(.*)"), "<li>\\1</li>")
|
||||||
|
.join("\n")
|
||||||
|
+ "</ul>"
|
||||||
|
+ tr("<p>Ignore?</p>"),
|
||||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||||
== QMessageBox::No)
|
== QMessageBox::No)
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the data from the "local working copy of stream" to "actual stream"
|
return true;
|
||||||
mpStream->protoDataCopyInto(s);
|
}
|
||||||
mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s);
|
|
||||||
|
void StreamConfigDialog::on_pbPrev_clicked()
|
||||||
|
{
|
||||||
|
Q_ASSERT(mCurrentStreamIndex > 0);
|
||||||
|
|
||||||
|
StoreCurrentStream();
|
||||||
|
|
||||||
|
if (!isCurrentStreamValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
delete _iter;
|
||||||
|
mpStream = _streamList.at(--mCurrentStreamIndex);
|
||||||
|
_iter = mpStream->createProtocolListIterator();
|
||||||
|
|
||||||
|
LoadCurrentStream();
|
||||||
|
on_twTopLevel_currentChanged(twTopLevel->currentIndex());
|
||||||
|
|
||||||
|
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||||
|
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamConfigDialog::on_pbNext_clicked()
|
||||||
|
{
|
||||||
|
Q_ASSERT(int(mCurrentStreamIndex) < (_streamList.size()-1));
|
||||||
|
|
||||||
|
StoreCurrentStream();
|
||||||
|
|
||||||
|
if (!isCurrentStreamValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
delete _iter;
|
||||||
|
mpStream = _streamList.at(++mCurrentStreamIndex);
|
||||||
|
_iter = mpStream->createProtocolListIterator();
|
||||||
|
|
||||||
|
LoadCurrentStream();
|
||||||
|
on_twTopLevel_currentChanged(twTopLevel->currentIndex());
|
||||||
|
|
||||||
|
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||||
|
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamConfigDialog::on_pbOk_clicked()
|
||||||
|
{
|
||||||
|
// Store dialog contents into current stream
|
||||||
|
StoreCurrentStream();
|
||||||
|
|
||||||
|
if (!isCurrentStreamValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Copy the working copy of streams to user provided streams
|
||||||
|
Q_ASSERT(_userStreamList.size() == _streamList.size());
|
||||||
|
for (int i = 0; i < _streamList.size(); i++) {
|
||||||
|
OstProto::Stream s;
|
||||||
|
_streamList.at(i)->protoDataCopyInto(s);
|
||||||
|
_userStreamList[i]->protoDataCopyFrom(s);
|
||||||
|
}
|
||||||
|
|
||||||
qDebug("stream stored");
|
qDebug("stream stored");
|
||||||
|
|
||||||
|
@ -43,9 +43,12 @@ class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0);
|
StreamConfigDialog(QList<Stream*> &streamList, const Port &port,
|
||||||
|
QWidget *parent = 0);
|
||||||
~StreamConfigDialog();
|
~StreamConfigDialog();
|
||||||
|
|
||||||
|
void setWindowTitle(const QString &title);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum ButtonId
|
enum ButtonId
|
||||||
@ -74,7 +77,10 @@ private:
|
|||||||
QStringListModel *mpAvailableProtocolsModel;
|
QStringListModel *mpAvailableProtocolsModel;
|
||||||
QStringListModel *mpSelectedProtocolsModel;
|
QStringListModel *mpSelectedProtocolsModel;
|
||||||
|
|
||||||
Port& mPort;
|
QList<Stream*> _userStreamList;
|
||||||
|
QList<Stream*> _streamList;
|
||||||
|
const Port& mPort;
|
||||||
|
QString _windowTitle;
|
||||||
uint mCurrentStreamIndex;
|
uint mCurrentStreamIndex;
|
||||||
|
|
||||||
Stream *mpStream;
|
Stream *mpStream;
|
||||||
@ -94,6 +100,7 @@ private:
|
|||||||
static int lastProtocolDataIndex;
|
static int lastProtocolDataIndex;
|
||||||
|
|
||||||
void setupUiExtra();
|
void setupUiExtra();
|
||||||
|
bool isCurrentStreamValid();
|
||||||
void LoadCurrentStream();
|
void LoadCurrentStream();
|
||||||
void StoreCurrentStream();
|
void StoreCurrentStream();
|
||||||
void loadProtocolWidgets();
|
void loadProtocolWidgets();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>StreamConfigDialog</class>
|
<class>StreamConfigDialog</class>
|
||||||
<widget class="QDialog" name="StreamConfigDialog">
|
<widget class="QDialog" name="StreamConfigDialog">
|
||||||
@ -13,7 +14,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -22,14 +23,15 @@
|
|||||||
<string>Edit Stream</string>
|
<string>Edit Stream</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/stream_edit.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string>QLineEdit:enabled[inputMask = "HH; "],
|
<string>QLineEdit:enabled[inputMask = "HH; "],
|
||||||
QLineEdit:enabled[inputMask = "HH HH; "],
|
QLineEdit:enabled[inputMask = "HH HH; "],
|
||||||
QLineEdit:enabled[inputMask = "HH HH HH; "],
|
QLineEdit:enabled[inputMask = "HH HH HH; "],
|
||||||
QLineEdit:enabled[inputMask = "HH HH HH HH; "], 
|
QLineEdit:enabled[inputMask = "HH HH HH HH; "],
|
||||||
QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } 
|
QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff }
|
||||||
</string>
|
</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="modal">
|
<property name="modal">
|
||||||
@ -49,20 +51,36 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<string>Protocol Selection</string>
|
<string>Protocol Selection</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout">
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Basics</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<spacer>
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="orientation" >
|
<property name="text">
|
||||||
<enum>Qt::Horizontal</enum>
|
<string>Name</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="buddy">
|
||||||
<size>
|
<cstring>name</cstring>
|
||||||
<width>241</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="name"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="enabled">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enabled</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
<widget class="QGroupBox" name="gbFrameLength">
|
<widget class="QGroupBox" name="gbFrameLength">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Frame Length (including FCS)</string>
|
<string>Frame Length (including FCS)</string>
|
||||||
@ -136,7 +154,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="2" >
|
<item row="1" column="0" colspan="3">
|
||||||
<widget class="QToolBox" name="tbSelectProtocols">
|
<widget class="QToolBox" name="tbSelectProtocols">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -698,7 +716,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
@ -712,10 +730,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>></string>
|
<string>></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/arrow_right.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/arrow_right.png</normaloff>:/icons/arrow_right.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -724,7 +743,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
@ -754,7 +773,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<string>^</string>
|
<string>^</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/arrow_up.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/arrow_up.png</normaloff>:/icons/arrow_up.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -767,7 +787,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<string>v</string>
|
<string>v</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/arrow_down.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/arrow_down.png</normaloff>:/icons/arrow_down.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -780,7 +801,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<string>-</string>
|
<string>-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="ostinato.qrc" >:/icons/delete.png</iconset>
|
<iconset resource="ostinato.qrc">
|
||||||
|
<normaloff>:/icons/delete.png</normaloff>:/icons/delete.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -789,7 +811,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>40</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
@ -834,7 +856,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QHBoxLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="VariableFieldsWidget" native="1" name="variableFieldsWidget" />
|
<widget class="VariableFieldsWidget" name="variableFieldsWidget" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -869,7 +891,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item rowspan="2" row="0" column="1" >
|
<item row="0" column="1" rowspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_13">
|
<widget class="QGroupBox" name="groupBox_13">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Numbers</string>
|
<string>Numbers</string>
|
||||||
@ -941,7 +963,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item rowspan="2" row="0" column="2" >
|
<item row="0" column="2" rowspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_14">
|
<widget class="QGroupBox" name="groupBox_14">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Rate</string>
|
<string>Rate</string>
|
||||||
@ -1010,7 +1032,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item rowspan="2" row="0" column="3" >
|
<item row="0" column="3" rowspan="2">
|
||||||
<widget class="QGroupBox" name="nextWhat">
|
<widget class="QGroupBox" name="nextWhat">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>After this stream</string>
|
<string>After this stream</string>
|
||||||
@ -1053,12 +1075,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item rowspan="2" row="0" column="4" >
|
<item row="0" column="4" rowspan="2">
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>41</height>
|
<height>41</height>
|
||||||
@ -1185,7 +1207,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>153</width>
|
<width>153</width>
|
||||||
<height>21</height>
|
<height>21</height>
|
||||||
@ -1216,7 +1238,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="DumpView" native="1" name="vwPacketDump" />
|
<widget class="DumpView" name="vwPacketDump" native="true"/>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -1244,7 +1266,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>191</width>
|
<width>191</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
|
@ -155,19 +155,21 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||||||
{
|
{
|
||||||
// Edit Supported Fields
|
// Edit Supported Fields
|
||||||
case StreamName:
|
case StreamName:
|
||||||
mCurrentPort->streamByIndex(index.row())->setName(value.toString());
|
mCurrentPort->mutableStreamByIndex(index.row())
|
||||||
|
->setName(value.toString());
|
||||||
emit(dataChanged(index, index));
|
emit(dataChanged(index, index));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case StreamStatus:
|
case StreamStatus:
|
||||||
mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool());
|
mCurrentPort->mutableStreamByIndex(index.row())
|
||||||
|
->setEnabled(value.toBool());
|
||||||
emit(dataChanged(index, index));
|
emit(dataChanged(index, index));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case StreamNextWhat:
|
case StreamNextWhat:
|
||||||
if (role == Qt::EditRole)
|
if (role == Qt::EditRole)
|
||||||
{
|
{
|
||||||
mCurrentPort->streamByIndex(index.row())->setNextWhat(
|
mCurrentPort->mutableStreamByIndex(index.row())->setNextWhat(
|
||||||
(Stream::NextWhat)value.toInt());
|
(Stream::NextWhat)value.toInt());
|
||||||
emit(dataChanged(index, index));
|
emit(dataChanged(index, index));
|
||||||
return true;
|
return true;
|
||||||
@ -221,6 +223,30 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Inserts streams before the given row
|
||||||
|
*
|
||||||
|
* StreamModel takes ownership of the passed streams; caller should
|
||||||
|
* not try to access them after calling this function
|
||||||
|
*/
|
||||||
|
bool StreamModel::insert(int row, QList<Stream*> &streams)
|
||||||
|
{
|
||||||
|
int count = streams.size();
|
||||||
|
qDebug("insert row = %d", row);
|
||||||
|
qDebug("insert count = %d", count);
|
||||||
|
beginInsertRows(QModelIndex(), row, row+count-1);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
OstProto::Stream s;
|
||||||
|
streams.at(i)->protoDataCopyInto(s);
|
||||||
|
mCurrentPort->newStreamAt(row+i, &s);
|
||||||
|
delete streams.at(i);
|
||||||
|
}
|
||||||
|
streams.clear();
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
|
bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
|
||||||
{
|
{
|
||||||
qDebug("insertRows() row = %d", row);
|
qDebug("insertRows() row = %d", row);
|
||||||
|
@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "port.h"
|
#include "port.h"
|
||||||
|
|
||||||
class PortGroupList;
|
class PortGroupList;
|
||||||
|
class Stream;
|
||||||
|
|
||||||
class StreamModel : public QAbstractTableModel
|
class StreamModel : public QAbstractTableModel
|
||||||
{
|
{
|
||||||
@ -44,6 +45,7 @@ class StreamModel : public QAbstractTableModel
|
|||||||
int role = Qt::EditRole);
|
int role = Qt::EditRole);
|
||||||
QVariant headerData(int section, Qt::Orientation orientation,
|
QVariant headerData(int section, Qt::Orientation orientation,
|
||||||
int role = Qt::DisplayRole) const;
|
int role = Qt::DisplayRole) const;
|
||||||
|
bool insert(int row, QList<Stream*> &streams);
|
||||||
bool insertRows (int row, int count,
|
bool insertRows (int row, int count,
|
||||||
const QModelIndex & parent = QModelIndex());
|
const QModelIndex & parent = QModelIndex());
|
||||||
bool removeRows (int row, int count,
|
bool removeRows (int row, int count,
|
||||||
|
48
client/xtableview.h
Normal file
48
client/xtableview.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2017 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _X_TABLE_VIEW_H
|
||||||
|
#define _X_TABLE_VIEW_H
|
||||||
|
|
||||||
|
#include <QTableView>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
class XTableView : public QTableView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XTableView(QWidget *parent) : QTableView(parent) {}
|
||||||
|
virtual ~XTableView() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
if (!model()->hasChildren()) {
|
||||||
|
QPainter painter(viewport());
|
||||||
|
style()->drawItemText(&painter, viewport()->rect(),
|
||||||
|
layoutDirection() | Qt::AlignCenter, palette(),
|
||||||
|
true, whatsThis(), QPalette::WindowText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
QTableView::paintEvent(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
50
client/xtreeview.h
Normal file
50
client/xtreeview.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2017 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _X_TREE_VIEW_H
|
||||||
|
#define _X_TREE_VIEW_H
|
||||||
|
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x050000
|
||||||
|
#error "Do we even need this anymore?"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class XTreeView : public QTreeView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XTreeView(QWidget *parent) : QTreeView(parent) {}
|
||||||
|
virtual ~XTreeView() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QModelIndex item = indexAt(event->pos());
|
||||||
|
|
||||||
|
if (!item.isValid())
|
||||||
|
setCurrentIndex(QModelIndex());
|
||||||
|
|
||||||
|
QTreeView::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,3 +1,4 @@
|
|||||||
|
/// (802.2 LLC)
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2010 Srivats P.
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
@ -23,7 +24,6 @@ import "llc.proto";
|
|||||||
|
|
||||||
package OstProto;
|
package OstProto;
|
||||||
|
|
||||||
// 802.2 LLC
|
|
||||||
message Dot2Llc {
|
message Dot2Llc {
|
||||||
// Empty since this is a 'combo' protocol
|
// Empty since this is a 'combo' protocol
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/// (802.2 SNAP)
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2010 Srivats P.
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/// (802.3)
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2010 Srivats P.
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ message File {
|
|||||||
// FieldNumber 1 is reserved and MUST not be used!
|
// FieldNumber 1 is reserved and MUST not be used!
|
||||||
required bytes magic_value = 2;
|
required bytes magic_value = 2;
|
||||||
required FileMetaData meta_data = 3;
|
required FileMetaData meta_data = 3;
|
||||||
optional FileContent content_matter = 9;
|
optional FileContentMatter content_matter = 9;
|
||||||
required fixed32 checksum_value = 15;
|
required fixed32 checksum_value = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import "protocol.proto";
|
|||||||
|
|
||||||
package OstProto;
|
package OstProto;
|
||||||
|
|
||||||
// Group Management Protocol (i.e. IGMP and MLD)
|
/// Group Management Protocol (i.e. IGMP and MLD)
|
||||||
message Gmp {
|
message Gmp {
|
||||||
//
|
//
|
||||||
// Common fields for both ASM and SSM messages
|
// Common fields for both ASM and SSM messages
|
||||||
|
@ -112,6 +112,7 @@ AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const
|
|||||||
|
|
||||||
case ip4_srcAddr:
|
case ip4_srcAddr:
|
||||||
case ip4_dstAddr:
|
case ip4_dstAddr:
|
||||||
|
case ip4_options:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ip4_isOverrideVer:
|
case ip4_isOverrideVer:
|
||||||
@ -168,7 +169,9 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
{
|
{
|
||||||
int hdrlen;
|
int hdrlen;
|
||||||
|
|
||||||
hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5;
|
hdrlen = data.is_override_hdrlen() ?
|
||||||
|
data.ver_hdrlen() : 5 + data.options().length()/4;
|
||||||
|
hdrlen &= 0x0F;
|
||||||
|
|
||||||
switch(attrib)
|
switch(attrib)
|
||||||
{
|
{
|
||||||
@ -205,6 +208,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
break;
|
break;
|
||||||
case ip4_totLen:
|
case ip4_totLen:
|
||||||
{
|
{
|
||||||
|
int ipLen = 20 + data.options().length();
|
||||||
switch(attrib)
|
switch(attrib)
|
||||||
{
|
{
|
||||||
case FieldName:
|
case FieldName:
|
||||||
@ -213,7 +217,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
{
|
{
|
||||||
int totlen;
|
int totlen;
|
||||||
totlen = data.is_override_totlen() ? data.totlen() :
|
totlen = data.is_override_totlen() ? data.totlen() :
|
||||||
(protocolFramePayloadSize(streamIndex) + 20);
|
(protocolFramePayloadSize(streamIndex) + ipLen);
|
||||||
return totlen;
|
return totlen;
|
||||||
}
|
}
|
||||||
case FieldFrameValue:
|
case FieldFrameValue:
|
||||||
@ -221,7 +225,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
QByteArray fv;
|
QByteArray fv;
|
||||||
int totlen;
|
int totlen;
|
||||||
totlen = data.is_override_totlen() ? data.totlen() :
|
totlen = data.is_override_totlen() ? data.totlen() :
|
||||||
(protocolFramePayloadSize(streamIndex) + 20);
|
(protocolFramePayloadSize(streamIndex) + ipLen);
|
||||||
fv.resize(2);
|
fv.resize(2);
|
||||||
qToBigEndian((quint16) totlen, (uchar*) fv.data());
|
qToBigEndian((quint16) totlen, (uchar*) fv.data());
|
||||||
return fv;
|
return fv;
|
||||||
@ -230,7 +234,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
{
|
{
|
||||||
int totlen;
|
int totlen;
|
||||||
totlen = data.is_override_totlen() ? data.totlen() :
|
totlen = data.is_override_totlen() ? data.totlen() :
|
||||||
(protocolFramePayloadSize(streamIndex) + 20);
|
(protocolFramePayloadSize(streamIndex) + ipLen);
|
||||||
return QString("%1").arg(totlen);
|
return QString("%1").arg(totlen);
|
||||||
}
|
}
|
||||||
case FieldBitSize:
|
case FieldBitSize:
|
||||||
@ -509,6 +513,32 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ip4_options:
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldValue:
|
||||||
|
case FieldFrameValue:
|
||||||
|
case FieldTextValue:
|
||||||
|
ba.append(QString().fromStdString(data.options()));
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch(attrib)
|
||||||
|
{
|
||||||
|
case FieldName:
|
||||||
|
return QString("Options");
|
||||||
|
case FieldValue:
|
||||||
|
case FieldFrameValue:
|
||||||
|
return ba;
|
||||||
|
case FieldTextValue:
|
||||||
|
return ba.toHex();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Meta fields
|
// Meta fields
|
||||||
case ip4_isOverrideVer:
|
case ip4_isOverrideVer:
|
||||||
@ -695,6 +725,16 @@ bool Ip4Protocol::setFieldData(int index, const QVariant &value,
|
|||||||
data.set_dst_ip(dstIp);
|
data.set_dst_ip(dstIp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ip4_options:
|
||||||
|
{
|
||||||
|
QByteArray ba = value.toByteArray();
|
||||||
|
int pad = (4 - (ba.size() % 4)) % 4;
|
||||||
|
if (pad)
|
||||||
|
ba.append(QByteArray(pad, 0));
|
||||||
|
data.set_options(ba.constData(), ba.size());
|
||||||
|
isOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Meta-fields
|
// Meta-fields
|
||||||
case ip4_isOverrideVer:
|
case ip4_isOverrideVer:
|
||||||
|
@ -44,6 +44,7 @@ public:
|
|||||||
ip4_cksum,
|
ip4_cksum,
|
||||||
ip4_srcAddr,
|
ip4_srcAddr,
|
||||||
ip4_dstAddr,
|
ip4_dstAddr,
|
||||||
|
ip4_options,
|
||||||
|
|
||||||
// Meta-fields
|
// Meta-fields
|
||||||
ip4_isOverrideVer,
|
ip4_isOverrideVer,
|
||||||
|
@ -58,7 +58,7 @@ message Ip4 {
|
|||||||
optional uint32 dst_ip_count = 20 [default = 16];
|
optional uint32 dst_ip_count = 20 [default = 16];
|
||||||
optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00];
|
optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00];
|
||||||
|
|
||||||
//! \todo (LOW) IPv4 Options
|
optional bytes options = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
extend Protocol {
|
extend Protocol {
|
||||||
|
@ -366,14 +366,7 @@ Length (x4)</string>
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="leIpOptions" >
|
<widget class="QLineEdit" name="leIpOptions" />
|
||||||
<property name="enabled" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string>TODO</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="tbIpOptionsEdit" >
|
<widget class="QToolButton" name="tbIpOptionsEdit" >
|
||||||
|
@ -28,6 +28,8 @@ Ip4ConfigForm::Ip4ConfigForm(QWidget *parent)
|
|||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
leIpVersion->setValidator(new QIntValidator(0, 15, this));
|
leIpVersion->setValidator(new QIntValidator(0, 15, this));
|
||||||
|
leIpOptions->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F]*"),
|
||||||
|
this));
|
||||||
|
|
||||||
connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)),
|
connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)),
|
||||||
this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int)));
|
this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int)));
|
||||||
@ -176,6 +178,11 @@ void Ip4ConfigForm::loadWidget(AbstractProtocol *proto)
|
|||||||
Ip4Protocol::ip4_dstAddrMask,
|
Ip4Protocol::ip4_dstAddrMask,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toUInt()).toString());
|
).toUInt()).toString());
|
||||||
|
leIpOptions->setText(
|
||||||
|
proto->fieldData(
|
||||||
|
Ip4Protocol::ip4_options,
|
||||||
|
AbstractProtocol::FieldValue
|
||||||
|
).toByteArray().toHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ip4ConfigForm::storeWidget(AbstractProtocol *proto)
|
void Ip4ConfigForm::storeWidget(AbstractProtocol *proto)
|
||||||
@ -263,6 +270,9 @@ void Ip4ConfigForm::storeWidget(AbstractProtocol *proto)
|
|||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
Ip4Protocol::ip4_dstAddrMask,
|
Ip4Protocol::ip4_dstAddrMask,
|
||||||
QHostAddress(leIpDstAddrMask->text()).toIPv4Address());
|
QHostAddress(leIpDstAddrMask->text()).toIPv4Address());
|
||||||
|
proto->setFieldData(
|
||||||
|
Ip4Protocol::ip4_options,
|
||||||
|
QByteArray::fromHex(QByteArray().append(leIpOptions->text())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -26,7 +26,6 @@ PdmlIp4Protocol::PdmlIp4Protocol()
|
|||||||
{
|
{
|
||||||
ostProtoId_ = OstProto::Protocol::kIp4FieldNumber;
|
ostProtoId_ = OstProto::Protocol::kIp4FieldNumber;
|
||||||
|
|
||||||
fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber);
|
|
||||||
fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber);
|
fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber);
|
||||||
fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber);
|
fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber);
|
||||||
fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber);
|
fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber);
|
||||||
@ -50,7 +49,18 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/,
|
|||||||
{
|
{
|
||||||
bool isOk;
|
bool isOk;
|
||||||
|
|
||||||
if ((name == "ip.options") ||
|
if (name == "ip.version")
|
||||||
|
{
|
||||||
|
OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4);
|
||||||
|
|
||||||
|
if (!attributes.value("unmaskedvalue").isEmpty())
|
||||||
|
ip4->set_ver_hdrlen(attributes.value("unmaskedvalue")
|
||||||
|
.toString().toUInt(&isOk, kBaseHex));
|
||||||
|
else
|
||||||
|
ip4->set_ver_hdrlen(attributes.value("value")
|
||||||
|
.toString().toUInt(&isOk, kBaseHex));
|
||||||
|
}
|
||||||
|
else if ((name == "ip.options") ||
|
||||||
attributes.value("show").toString().startsWith("Options"))
|
attributes.value("show").toString().startsWith("Options"))
|
||||||
{
|
{
|
||||||
options_ = QByteArray::fromHex(
|
options_ = QByteArray::fromHex(
|
||||||
|
@ -26,7 +26,9 @@ PdmlIp6Protocol::PdmlIp6Protocol()
|
|||||||
ostProtoId_ = OstProto::Protocol::kIp6FieldNumber;
|
ostProtoId_ = OstProto::Protocol::kIp6FieldNumber;
|
||||||
|
|
||||||
fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber);
|
fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber);
|
||||||
|
// Tshark 1.x uses .class while 2.x uses .tclass - we'll use either
|
||||||
fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber);
|
fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber);
|
||||||
|
fieldMap_.insert("ipv6.tclass", OstProto::Ip6::kTrafficClassFieldNumber);
|
||||||
fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber);
|
fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber);
|
||||||
fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber);
|
fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber);
|
||||||
fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber);
|
fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber);
|
||||||
|
@ -143,7 +143,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib,
|
|||||||
switch(attrib)
|
switch(attrib)
|
||||||
{
|
{
|
||||||
case FieldName:
|
case FieldName:
|
||||||
return QString("Desination");
|
return QString("Destination");
|
||||||
case FieldValue:
|
case FieldValue:
|
||||||
return dstMac;
|
return dstMac;
|
||||||
case FieldTextValue:
|
case FieldTextValue:
|
||||||
@ -278,15 +278,13 @@ bool MacProtocol::setFieldData(int index, const QVariant &value,
|
|||||||
{
|
{
|
||||||
case mac_dstAddr:
|
case mac_dstAddr:
|
||||||
{
|
{
|
||||||
quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX);
|
quint64 mac = value.toULongLong();
|
||||||
if (isOk)
|
|
||||||
data.set_dst_mac(mac);
|
data.set_dst_mac(mac);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case mac_srcAddr:
|
case mac_srcAddr:
|
||||||
{
|
{
|
||||||
quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX);
|
quint64 mac = value.toULongLong();
|
||||||
if (isOk)
|
|
||||||
data.set_src_mac(mac);
|
data.set_src_mac(mac);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>mac</class>
|
<class>mac</class>
|
||||||
<widget class="QWidget" name="mac">
|
<widget class="QWidget" name="mac">
|
||||||
@ -49,19 +50,13 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="leDstMac" >
|
<widget class="MacEdit" name="leDstMac">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>120</width>
|
<width>120</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="inputMask" >
|
|
||||||
<string>>HH HH HH HH HH HH; </string>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string> </string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="1" column="2">
|
||||||
@ -89,29 +84,17 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="3">
|
<item row="1" column="3">
|
||||||
<widget class="QLineEdit" name="leDstMacCount" >
|
<widget class="IntEdit" name="leDstMacCount">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="cursorPosition" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="4">
|
<item row="1" column="4">
|
||||||
<widget class="QLineEdit" name="leDstMacStep" >
|
<widget class="IntEdit" name="leDstMacStep">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="cursorPosition" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
@ -122,14 +105,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="leSrcMac" >
|
<widget class="MacEdit" name="leSrcMac"/>
|
||||||
<property name="inputMask" >
|
|
||||||
<string>>HH HH HH HH HH HH; </string>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string> </string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QComboBox" name="cmbSrcMacMode">
|
<widget class="QComboBox" name="cmbSrcMacMode">
|
||||||
@ -156,26 +132,17 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="3">
|
<item row="2" column="3">
|
||||||
<widget class="QLineEdit" name="leSrcMacCount" >
|
<widget class="IntEdit" name="leSrcMacCount">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="4">
|
<item row="2" column="4">
|
||||||
<widget class="QLineEdit" name="leSrcMacStep" >
|
<widget class="IntEdit" name="leSrcMacStep">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="cursorPosition" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="5">
|
<item row="3" column="0" colspan="5">
|
||||||
@ -193,7 +160,7 @@
|
|||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" >
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
@ -203,6 +170,18 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>IntEdit</class>
|
||||||
|
<extends>QSpinBox</extends>
|
||||||
|
<header>intedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>MacEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>macedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -20,12 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#include "macconfig.h"
|
#include "macconfig.h"
|
||||||
#include "mac.h"
|
#include "mac.h"
|
||||||
|
|
||||||
#define MAX_MAC_ITER_COUNT 256
|
|
||||||
|
|
||||||
MacConfigForm::MacConfigForm(QWidget *parent)
|
MacConfigForm::MacConfigForm(QWidget *parent)
|
||||||
: AbstractProtocolConfigForm(parent)
|
: AbstractProtocolConfigForm(parent)
|
||||||
{
|
{
|
||||||
QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}");
|
|
||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
resolveInfo->hide();
|
resolveInfo->hide();
|
||||||
@ -34,10 +31,10 @@ MacConfigForm::MacConfigForm(QWidget *parent)
|
|||||||
resolveInfo->setPixmap(resolveInfo->style()->standardIcon(
|
resolveInfo->setPixmap(resolveInfo->style()->standardIcon(
|
||||||
QStyle::SP_MessageBoxInformation).pixmap(128));
|
QStyle::SP_MessageBoxInformation).pixmap(128));
|
||||||
#endif
|
#endif
|
||||||
leDstMac->setValidator(new QRegExpValidator(reMac, this));
|
leDstMacCount->setMinimum(1);
|
||||||
leSrcMac->setValidator(new QRegExpValidator(reMac, this));
|
leSrcMacCount->setMinimum(1);
|
||||||
leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this));
|
leDstMacStep->setMinimum(0);
|
||||||
leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this));
|
leSrcMacStep->setMinimum(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
MacConfigForm::~MacConfigForm()
|
MacConfigForm::~MacConfigForm()
|
||||||
@ -100,75 +97,75 @@ void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index)
|
|||||||
|
|
||||||
void MacConfigForm::loadWidget(AbstractProtocol *proto)
|
void MacConfigForm::loadWidget(AbstractProtocol *proto)
|
||||||
{
|
{
|
||||||
leDstMac->setText(
|
leDstMac->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_dstAddr,
|
MacProtocol::mac_dstAddr,
|
||||||
AbstractProtocol::FieldTextValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toULongLong());
|
||||||
cmbDstMacMode->setCurrentIndex(
|
cmbDstMacMode->setCurrentIndex(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_dstMacMode,
|
MacProtocol::mac_dstMacMode,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toUInt());
|
).toUInt());
|
||||||
leDstMacCount->setText(
|
leDstMacCount->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_dstMacCount,
|
MacProtocol::mac_dstMacCount,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toUInt());
|
||||||
leDstMacStep->setText(
|
leDstMacStep->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_dstMacStep,
|
MacProtocol::mac_dstMacStep,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toUInt());
|
||||||
|
|
||||||
leSrcMac->setText(
|
leSrcMac->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_srcAddr,
|
MacProtocol::mac_srcAddr,
|
||||||
AbstractProtocol::FieldTextValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toULongLong());
|
||||||
cmbSrcMacMode->setCurrentIndex(
|
cmbSrcMacMode->setCurrentIndex(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_srcMacMode,
|
MacProtocol::mac_srcMacMode,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toUInt());
|
).toUInt());
|
||||||
leSrcMacCount->setText(
|
leSrcMacCount->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_srcMacCount,
|
MacProtocol::mac_srcMacCount,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toUInt());
|
||||||
leSrcMacStep->setText(
|
leSrcMacStep->setValue(
|
||||||
proto->fieldData(
|
proto->fieldData(
|
||||||
MacProtocol::mac_srcMacStep,
|
MacProtocol::mac_srcMacStep,
|
||||||
AbstractProtocol::FieldValue
|
AbstractProtocol::FieldValue
|
||||||
).toString());
|
).toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacConfigForm::storeWidget(AbstractProtocol *proto)
|
void MacConfigForm::storeWidget(AbstractProtocol *proto)
|
||||||
{
|
{
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_dstAddr,
|
MacProtocol::mac_dstAddr,
|
||||||
leDstMac->text().remove(QChar(' ')));
|
leDstMac->value());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_dstMacMode,
|
MacProtocol::mac_dstMacMode,
|
||||||
cmbDstMacMode->currentIndex());
|
cmbDstMacMode->currentIndex());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_dstMacCount,
|
MacProtocol::mac_dstMacCount,
|
||||||
leDstMacCount->text());
|
leDstMacCount->value());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_dstMacStep,
|
MacProtocol::mac_dstMacStep,
|
||||||
leDstMacStep->text());
|
leDstMacStep->value());
|
||||||
|
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_srcAddr,
|
MacProtocol::mac_srcAddr,
|
||||||
leSrcMac->text().remove(QChar(' ')));
|
leSrcMac->value());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_srcMacMode,
|
MacProtocol::mac_srcMacMode,
|
||||||
cmbSrcMacMode->currentIndex());
|
cmbSrcMacMode->currentIndex());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_srcMacCount,
|
MacProtocol::mac_srcMacCount,
|
||||||
leSrcMacCount->text());
|
leSrcMacCount->value());
|
||||||
proto->setFieldData(
|
proto->setFieldData(
|
||||||
MacProtocol::mac_srcMacStep,
|
MacProtocol::mac_srcMacStep,
|
||||||
leSrcMacStep->text());
|
leSrcMacStep->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ message StreamCore {
|
|||||||
optional bool is_enabled = 2;
|
optional bool is_enabled = 2;
|
||||||
optional uint32 ordinal = 3;
|
optional uint32 ordinal = 3;
|
||||||
|
|
||||||
// Frame Length (includes CRC)
|
|
||||||
optional FrameLengthMode len_mode = 14 [default = e_fl_fixed];
|
optional FrameLengthMode len_mode = 14 [default = e_fl_fixed];
|
||||||
|
/// Frame Length (includes CRC)
|
||||||
optional uint32 frame_len = 15 [default = 64];
|
optional uint32 frame_len = 15 [default = 64];
|
||||||
optional uint32 frame_len_min = 16 [default = 64];
|
optional uint32 frame_len_min = 16 [default = 64];
|
||||||
optional uint32 frame_len_max = 17 [default = 1518];
|
optional uint32 frame_len_max = 17 [default = 1518];
|
||||||
@ -78,7 +78,7 @@ message StreamControl {
|
|||||||
|
|
||||||
optional SendUnit unit = 1 [default = e_su_packets];
|
optional SendUnit unit = 1 [default = e_su_packets];
|
||||||
optional SendMode mode = 2 [default = e_sm_fixed];
|
optional SendMode mode = 2 [default = e_sm_fixed];
|
||||||
optional uint32 num_packets = 3 [default = 1];
|
optional uint32 num_packets = 3 [default = 10];
|
||||||
optional uint32 num_bursts = 4 [default = 1];
|
optional uint32 num_bursts = 4 [default = 1];
|
||||||
optional uint32 packets_per_burst = 5 [default = 10];
|
optional uint32 packets_per_burst = 5 [default = 10];
|
||||||
optional NextWhat next = 6 [default = e_nw_goto_next];
|
optional NextWhat next = 6 [default = e_nw_goto_next];
|
||||||
|
@ -40,5 +40,5 @@ message Stp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extend Protocol {
|
extend Protocol {
|
||||||
optional Stp stp = 210;
|
optional Stp stp = 209;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ ProtocolListIterator* StreamBase::createProtocolListIterator() const
|
|||||||
return new ProtocolListIterator(*currentFrameProtocols);
|
return new ProtocolListIterator(*currentFrameProtocols);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 StreamBase::id()
|
quint32 StreamBase::id() const
|
||||||
{
|
{
|
||||||
return mStreamId->id();
|
return mStreamId->id();
|
||||||
}
|
}
|
||||||
@ -598,7 +598,7 @@ quint64 StreamBase::neighborMacAddress(int frameIndex) const
|
|||||||
All errors found are returned. However, each type of error is reported
|
All errors found are returned. However, each type of error is reported
|
||||||
only once, even if multiple packets may have that error.
|
only once, even if multiple packets may have that error.
|
||||||
*/
|
*/
|
||||||
bool StreamBase::preflightCheck(QString &result) const
|
bool StreamBase::preflightCheck(QStringList &result) const
|
||||||
{
|
{
|
||||||
bool pass = true;
|
bool pass = true;
|
||||||
bool chkTrunc = true;
|
bool chkTrunc = true;
|
||||||
@ -611,8 +611,8 @@ bool StreamBase::preflightCheck(QString &result) const
|
|||||||
|
|
||||||
if (chkTrunc && (pktLen < (frameProtocolLength(i) + kFcsSize)))
|
if (chkTrunc && (pktLen < (frameProtocolLength(i) + kFcsSize)))
|
||||||
{
|
{
|
||||||
result += QString("* One or more frames may be truncated - "
|
result << QObject::tr("One or more frames may be truncated - "
|
||||||
"frame length should be at least %1\n")
|
"frame length should be at least %1")
|
||||||
.arg(frameProtocolLength(i) + kFcsSize);
|
.arg(frameProtocolLength(i) + kFcsSize);
|
||||||
chkTrunc = false;
|
chkTrunc = false;
|
||||||
pass = false;
|
pass = false;
|
||||||
@ -620,9 +620,8 @@ bool StreamBase::preflightCheck(QString &result) const
|
|||||||
|
|
||||||
if (chkJumbo && (pktLen > 1522))
|
if (chkJumbo && (pktLen > 1522))
|
||||||
{
|
{
|
||||||
result += QString("* Jumbo frames may be truncated or dropped "
|
result << QObject::tr("Jumbo frames may be truncated or dropped "
|
||||||
"if not supported by the hardware\n");
|
"if not supported by the hardware");
|
||||||
chkJumbo = false;
|
|
||||||
pass = false;
|
pass = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,6 +631,20 @@ bool StreamBase::preflightCheck(QString &result) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frameCount() <= averagePacketRate() && nextWhat() != e_nw_goto_id)
|
||||||
|
{
|
||||||
|
result << QObject::tr("Only %L1 frames at the rate of "
|
||||||
|
"%L2 frames/sec are configured to be transmitted. "
|
||||||
|
"Transmission will last for only %L3 second - "
|
||||||
|
"to transmit for a longer duration, "
|
||||||
|
"increase the number of packets (bursts) and/or "
|
||||||
|
"set the 'After this stream' action as 'Goto First'")
|
||||||
|
.arg(frameCount())
|
||||||
|
.arg(averagePacketRate(), 0, 'f', 2)
|
||||||
|
.arg(frameCount()/averagePacketRate(), 0, 'f');
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ public:
|
|||||||
e_nw_goto_id
|
e_nw_goto_id
|
||||||
};
|
};
|
||||||
|
|
||||||
quint32 id();
|
quint32 id() const;
|
||||||
bool setId(quint32 id);
|
bool setId(quint32 id);
|
||||||
|
|
||||||
#if 0 // FIXME(HI): needed?
|
#if 0 // FIXME(HI): needed?
|
||||||
@ -141,7 +141,7 @@ public:
|
|||||||
quint64 deviceMacAddress(int frameIndex) const;
|
quint64 deviceMacAddress(int frameIndex) const;
|
||||||
quint64 neighborMacAddress(int frameIndex) const;
|
quint64 neighborMacAddress(int frameIndex) const;
|
||||||
|
|
||||||
bool preflightCheck(QString &result) const;
|
bool preflightCheck(QStringList &result) const;
|
||||||
|
|
||||||
static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2);
|
static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
|||||||
|
|
||||||
QString TcpProtocol::name() const
|
QString TcpProtocol::name() const
|
||||||
{
|
{
|
||||||
return QString("Transmission Control Protocol");
|
return QString("Transmission Control Protocol (state less)");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TcpProtocol::shortName() const
|
QString TcpProtocol::shortName() const
|
||||||
|
@ -196,6 +196,29 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="0" colspan="5">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><i>Note: Ostinato is stateless- it cannot establish a TCP connection and generate seq/ack numbers accordingly</i></string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="2" colspan="2" >
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -37,6 +37,7 @@ Updater::Updater()
|
|||||||
Q_ASSERT(isVersionNewer("10.1", "2") == true);
|
Q_ASSERT(isVersionNewer("10.1", "2") == true);
|
||||||
Q_ASSERT(isVersionNewer("0.10", "0.2") == true);
|
Q_ASSERT(isVersionNewer("0.10", "0.2") == true);
|
||||||
Q_ASSERT(isVersionNewer("1.10.1", "1.2.3") == true);
|
Q_ASSERT(isVersionNewer("1.10.1", "1.2.3") == true);
|
||||||
|
Q_ASSERT(isVersionNewer("0.7.1", "0.8") == false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ void Updater::checkForNewVersion()
|
|||||||
file_ = new QTemporaryFile();
|
file_ = new QTemporaryFile();
|
||||||
|
|
||||||
reqHdr.setValue("Host", host);
|
reqHdr.setValue("Host", host);
|
||||||
reqHdr.setValue("UserAgent", userAgent());
|
reqHdr.setValue("User-Agent", userAgent());
|
||||||
|
|
||||||
connect(http_, SIGNAL(responseHeaderReceived(QHttpResponseHeader)),
|
connect(http_, SIGNAL(responseHeaderReceived(QHttpResponseHeader)),
|
||||||
this, SLOT(responseReceived(QHttpResponseHeader)));
|
this, SLOT(responseReceived(QHttpResponseHeader)));
|
||||||
@ -120,8 +121,12 @@ bool Updater::isVersionNewer(QString newVersion, QString curVersion)
|
|||||||
|
|
||||||
for (int i = 0; i < qMin(curVer.size(), newVer.size()); i++) {
|
for (int i = 0; i < qMin(curVer.size(), newVer.size()); i++) {
|
||||||
bool isOk;
|
bool isOk;
|
||||||
if (newVer.at(i).toUInt(&isOk) > curVer.at(i).toUInt(&isOk))
|
uint n = newVer.at(i).toUInt(&isOk);
|
||||||
|
uint c = curVer.at(i).toUInt(&isOk);
|
||||||
|
if (n > c)
|
||||||
return true;
|
return true;
|
||||||
|
else if (n < c)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newVer.size() > curVer.size())
|
if (newVer.size() > curVer.size())
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
TEMPLATE = lib
|
TEMPLATE = lib
|
||||||
CONFIG += qt staticlib warn_on
|
CONFIG += qt staticlib warn_on
|
||||||
|
QT += core gui
|
||||||
|
|
||||||
HEADERS = src/commands.h\
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
VERSION = 4.0.0
|
||||||
|
|
||||||
|
DEFINES += QHEXEDIT_EXPORTS
|
||||||
|
|
||||||
|
HEADERS = src/chunks.h\
|
||||||
|
src/commands.h \
|
||||||
src/qhexedit.h \
|
src/qhexedit.h \
|
||||||
src/qhexedit_p.h \
|
|
||||||
src/xbytearray.h
|
|
||||||
|
|
||||||
SOURCES = src/commands.cpp \
|
SOURCES = src/chunks.cpp \
|
||||||
src/qhexedit.cpp \
|
src/commands.cpp \
|
||||||
src/qhexedit_p.cpp \
|
src/qhexedit.cpp
|
||||||
src/xbytearray.cpp
|
|
||||||
|
1
extra/qhexedit2/src/VERSION
Normal file
1
extra/qhexedit2/src/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
Release 0.8.4, 2017-01-16
|
323
extra/qhexedit2/src/chunks.cpp
Normal file
323
extra/qhexedit2/src/chunks.cpp
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
#include "chunks.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define NORMAL 0
|
||||||
|
#define HIGHLIGHTED 1
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 0x10000
|
||||||
|
#define CHUNK_SIZE 0x1000
|
||||||
|
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
|
||||||
|
|
||||||
|
// ***************************************** Constructors and file settings
|
||||||
|
|
||||||
|
Chunks::Chunks(QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
setIODevice(*buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
setIODevice(ioDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::setIODevice(QIODevice &ioDevice)
|
||||||
|
{
|
||||||
|
_ioDevice = &ioDevice;
|
||||||
|
bool ok = _ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
if (ok) // Try to open IODevice
|
||||||
|
{
|
||||||
|
_size = _ioDevice->size();
|
||||||
|
_ioDevice->close();
|
||||||
|
}
|
||||||
|
else // Fallback is an empty buffer
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
_ioDevice = buf;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
_chunks.clear();
|
||||||
|
_pos = 0;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Getting data out of Chunks
|
||||||
|
|
||||||
|
QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
|
||||||
|
{
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
int chunkIdx = 0;
|
||||||
|
|
||||||
|
Chunk chunk;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
// Do some checks and some arrangements
|
||||||
|
if (highlighted)
|
||||||
|
highlighted->clear();
|
||||||
|
|
||||||
|
if (pos >= _size)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
if (maxSize < 0)
|
||||||
|
maxSize = _size;
|
||||||
|
else
|
||||||
|
if ((pos + maxSize) > _size)
|
||||||
|
maxSize = _size - pos;
|
||||||
|
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
while (maxSize > 0)
|
||||||
|
{
|
||||||
|
chunk.absPos = LLONG_MAX;
|
||||||
|
bool chunksLoopOngoing = true;
|
||||||
|
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing)
|
||||||
|
{
|
||||||
|
// In this section, we track changes before our required data and
|
||||||
|
// we take the editdet data, if availible. ioDelta is a difference
|
||||||
|
// counter to justify the read pointer to the original data, if
|
||||||
|
// data in between was deleted or inserted.
|
||||||
|
|
||||||
|
chunk = _chunks[chunkIdx];
|
||||||
|
if (chunk.absPos > pos)
|
||||||
|
chunksLoopOngoing = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chunkIdx += 1;
|
||||||
|
qint64 count;
|
||||||
|
qint64 chunkOfs = pos - chunk.absPos;
|
||||||
|
if (maxSize > ((qint64)chunk.data.size() - chunkOfs))
|
||||||
|
{
|
||||||
|
count = (qint64)chunk.data.size() - chunkOfs;
|
||||||
|
ioDelta += CHUNK_SIZE - chunk.data.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = maxSize;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
buffer += chunk.data.mid(chunkOfs, (int)count);
|
||||||
|
maxSize -= count;
|
||||||
|
pos += count;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += chunk.dataChanged.mid(chunkOfs, (int)count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((maxSize > 0) && (pos < chunk.absPos))
|
||||||
|
{
|
||||||
|
// In this section, we read data from the original source. This only will
|
||||||
|
// happen, whe no copied data is available
|
||||||
|
|
||||||
|
qint64 byteCount;
|
||||||
|
QByteArray readBuffer;
|
||||||
|
if ((chunk.absPos - pos) > maxSize)
|
||||||
|
byteCount = maxSize;
|
||||||
|
else
|
||||||
|
byteCount = chunk.absPos - pos;
|
||||||
|
|
||||||
|
maxSize -= byteCount;
|
||||||
|
_ioDevice->seek(pos + ioDelta);
|
||||||
|
readBuffer = _ioDevice->read(byteCount);
|
||||||
|
buffer += readBuffer;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += QByteArray(readBuffer.size(), NORMAL);
|
||||||
|
pos += readBuffer.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ioDevice->close();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
|
||||||
|
{
|
||||||
|
if (count == -1)
|
||||||
|
count = _size;
|
||||||
|
bool ok = iODevice.open(QIODevice::WriteOnly);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
QByteArray ba = data(idx, BUFFER_SIZE);
|
||||||
|
iODevice.write(ba);
|
||||||
|
}
|
||||||
|
iODevice.close();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Set and get highlighting infos
|
||||||
|
|
||||||
|
void Chunks::setDataChanged(qint64 pos, bool dataChanged)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::dataChanged(qint64 pos)
|
||||||
|
{
|
||||||
|
QByteArray highlighted;
|
||||||
|
data(pos, 1, &highlighted);
|
||||||
|
return bool(highlighted.at(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Search API
|
||||||
|
|
||||||
|
qint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
|
||||||
|
int findPos = buffer.indexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = pos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;
|
||||||
|
if (sPos < 0)
|
||||||
|
sPos = 0;
|
||||||
|
buffer = data(sPos, pos - sPos);
|
||||||
|
int findPos = buffer.lastIndexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = sPos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Char manipulations
|
||||||
|
|
||||||
|
bool Chunks::insert(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos > _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx;
|
||||||
|
if (pos == _size)
|
||||||
|
chunkIdx = getChunkIndex(pos-1);
|
||||||
|
else
|
||||||
|
chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.insert(posInBa, b);
|
||||||
|
_chunks[chunkIdx].dataChanged.insert(posInBa, char(1));
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos += 1;
|
||||||
|
_size += 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::overwrite(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data[(int)posInBa] = b;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::removeAt(qint64 pos)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.remove(posInBa, 1);
|
||||||
|
_chunks[chunkIdx].dataChanged.remove(posInBa, 1);
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos -= 1;
|
||||||
|
_size -= 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Utility functions
|
||||||
|
|
||||||
|
char Chunks::operator[](qint64 pos)
|
||||||
|
{
|
||||||
|
return data(pos, 1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::pos()
|
||||||
|
{
|
||||||
|
return _pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::size()
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Chunks::getChunkIndex(qint64 absPos)
|
||||||
|
{
|
||||||
|
// This routine checks, if there is already a copied chunk available. If os, it
|
||||||
|
// returns a reference to it. If there is no copied chunk available, original
|
||||||
|
// data will be copied into a new chunk.
|
||||||
|
|
||||||
|
int foundIdx = -1;
|
||||||
|
int insertIdx = 0;
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (int idx=0; idx < _chunks.size(); idx++)
|
||||||
|
{
|
||||||
|
Chunk chunk = _chunks[idx];
|
||||||
|
if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size())))
|
||||||
|
{
|
||||||
|
foundIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (absPos < chunk.absPos)
|
||||||
|
{
|
||||||
|
insertIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ioDelta += chunk.data.size() - CHUNK_SIZE;
|
||||||
|
insertIdx = idx + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundIdx == -1)
|
||||||
|
{
|
||||||
|
Chunk newChunk;
|
||||||
|
qint64 readAbsPos = absPos - ioDelta;
|
||||||
|
qint64 readPos = (readAbsPos & READ_CHUNK_MASK);
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
_ioDevice->seek(readPos);
|
||||||
|
newChunk.data = _ioDevice->read(CHUNK_SIZE);
|
||||||
|
_ioDevice->close();
|
||||||
|
newChunk.absPos = absPos - (readAbsPos - readPos);
|
||||||
|
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
||||||
|
_chunks.insert(insertIdx, newChunk);
|
||||||
|
foundIdx = insertIdx;
|
||||||
|
}
|
||||||
|
return foundIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
int Chunks::chunkSize()
|
||||||
|
{
|
||||||
|
return _chunks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
77
extra/qhexedit2/src/chunks.h
Normal file
77
extra/qhexedit2/src/chunks.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef CHUNKS_H
|
||||||
|
#define CHUNKS_H
|
||||||
|
|
||||||
|
/** \cond docNever */
|
||||||
|
|
||||||
|
/*! The Chunks class is the storage backend for QHexEdit.
|
||||||
|
*
|
||||||
|
* When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses
|
||||||
|
* a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data
|
||||||
|
* will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every
|
||||||
|
* access Chunks closes the QIODevice, that's why external applications can overwrite files while
|
||||||
|
* QHexEdit shows them.
|
||||||
|
*
|
||||||
|
* When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4
|
||||||
|
* kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk,
|
||||||
|
* which keep track of which bytes are changed and which not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
struct Chunk
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
QByteArray dataChanged;
|
||||||
|
qint64 absPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Chunks: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// Constructors and file settings
|
||||||
|
Chunks(QObject *parent);
|
||||||
|
Chunks(QIODevice &ioDevice, QObject *parent);
|
||||||
|
bool setIODevice(QIODevice &ioDevice);
|
||||||
|
|
||||||
|
// Getting data out of Chunks
|
||||||
|
QByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0);
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
// Set and get highlighting infos
|
||||||
|
void setDataChanged(qint64 pos, bool dataChanged);
|
||||||
|
bool dataChanged(qint64 pos);
|
||||||
|
|
||||||
|
// Search API
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
// Char manipulations
|
||||||
|
bool insert(qint64 pos, char b);
|
||||||
|
bool overwrite(qint64 pos, char b);
|
||||||
|
bool removeAt(qint64 pos);
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
char operator[](qint64 pos);
|
||||||
|
qint64 pos();
|
||||||
|
qint64 size();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getChunkIndex(qint64 absPos);
|
||||||
|
|
||||||
|
QIODevice * _ioDevice;
|
||||||
|
qint64 _pos;
|
||||||
|
qint64 _size;
|
||||||
|
QList<Chunk> _chunks;
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
public:
|
||||||
|
int chunkSize();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \endcond docNever */
|
||||||
|
|
||||||
|
#endif // CHUNKS_H
|
@ -1,9 +1,34 @@
|
|||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
#include <QUndoCommand>
|
||||||
|
|
||||||
CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent)
|
|
||||||
|
// Helper class to store single byte commands
|
||||||
|
class CharCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum CCmd {insert, removeAt, overwrite};
|
||||||
|
|
||||||
|
CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar,
|
||||||
|
QUndoCommand *parent=0);
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
bool mergeWith(const QUndoCommand *command);
|
||||||
|
int id() const { return 1234; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Chunks * _chunks;
|
||||||
|
qint64 _charPos;
|
||||||
|
bool _wasChanged;
|
||||||
|
char _newChar;
|
||||||
|
char _oldChar;
|
||||||
|
CCmd _cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
CharCommand::CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent)
|
||||||
: QUndoCommand(parent)
|
: QUndoCommand(parent)
|
||||||
{
|
{
|
||||||
_xData = xData;
|
_chunks = chunks;
|
||||||
_charPos = charPos;
|
_charPos = charPos;
|
||||||
_newChar = newChar;
|
_newChar = newChar;
|
||||||
_cmd = cmd;
|
_cmd = cmd;
|
||||||
@ -14,9 +39,9 @@ bool CharCommand::mergeWith(const QUndoCommand *command)
|
|||||||
const CharCommand *nextCommand = static_cast<const CharCommand *>(command);
|
const CharCommand *nextCommand = static_cast<const CharCommand *>(command);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (_cmd != remove)
|
if (_cmd != CharCommand::removeAt)
|
||||||
{
|
{
|
||||||
if (nextCommand->_cmd == replace)
|
if (nextCommand->_cmd == overwrite)
|
||||||
if (nextCommand->_charPos == _charPos)
|
if (nextCommand->_charPos == _charPos)
|
||||||
{
|
{
|
||||||
_newChar = nextCommand->_newChar;
|
_newChar = nextCommand->_newChar;
|
||||||
@ -31,15 +56,15 @@ void CharCommand::undo()
|
|||||||
switch (_cmd)
|
switch (_cmd)
|
||||||
{
|
{
|
||||||
case insert:
|
case insert:
|
||||||
_xData->remove(_charPos, 1);
|
_chunks->removeAt(_charPos);
|
||||||
break;
|
break;
|
||||||
case replace:
|
case overwrite:
|
||||||
_xData->replace(_charPos, _oldChar);
|
_chunks->overwrite(_charPos, _oldChar);
|
||||||
_xData->setDataChanged(_charPos, _wasChanged);
|
_chunks->setDataChanged(_charPos, _wasChanged);
|
||||||
break;
|
break;
|
||||||
case remove:
|
case removeAt:
|
||||||
_xData->insert(_charPos, _oldChar);
|
_chunks->insert(_charPos, _oldChar);
|
||||||
_xData->setDataChanged(_charPos, _wasChanged);
|
_chunks->setDataChanged(_charPos, _wasChanged);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,67 +74,92 @@ void CharCommand::redo()
|
|||||||
switch (_cmd)
|
switch (_cmd)
|
||||||
{
|
{
|
||||||
case insert:
|
case insert:
|
||||||
_xData->insert(_charPos, _newChar);
|
_chunks->insert(_charPos, _newChar);
|
||||||
break;
|
break;
|
||||||
case replace:
|
case overwrite:
|
||||||
_oldChar = _xData->data()[_charPos];
|
_oldChar = (*_chunks)[_charPos];
|
||||||
_wasChanged = _xData->dataChanged(_charPos);
|
_wasChanged = _chunks->dataChanged(_charPos);
|
||||||
_xData->replace(_charPos, _newChar);
|
_chunks->overwrite(_charPos, _newChar);
|
||||||
break;
|
break;
|
||||||
case remove:
|
case removeAt:
|
||||||
_oldChar = _xData->data()[_charPos];
|
_oldChar = (*_chunks)[_charPos];
|
||||||
_wasChanged = _xData->dataChanged(_charPos);
|
_wasChanged = _chunks->dataChanged(_charPos);
|
||||||
_xData->remove(_charPos, 1);
|
_chunks->removeAt(_charPos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UndoStack::UndoStack(Chunks * chunks, QObject * parent)
|
||||||
|
: QUndoStack(parent)
|
||||||
ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent)
|
|
||||||
: QUndoCommand(parent)
|
|
||||||
{
|
{
|
||||||
_cmd = cmd;
|
_chunks = chunks;
|
||||||
_xData = xData;
|
_parent = parent;
|
||||||
_baPos = baPos;
|
|
||||||
_newBa = newBa;
|
|
||||||
_len = len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayCommand::undo()
|
void UndoStack::insert(qint64 pos, char c)
|
||||||
{
|
{
|
||||||
switch (_cmd)
|
if ((pos >= 0) && (pos <= _chunks->size()))
|
||||||
{
|
{
|
||||||
case insert:
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos, c);
|
||||||
_xData->remove(_baPos, _newBa.length());
|
this->push(cc);
|
||||||
break;
|
|
||||||
case replace:
|
|
||||||
_xData->replace(_baPos, _oldBa);
|
|
||||||
_xData->setDataChanged(_baPos, _wasChanged);
|
|
||||||
break;
|
|
||||||
case remove:
|
|
||||||
_xData->insert(_baPos, _oldBa);
|
|
||||||
_xData->setDataChanged(_baPos, _wasChanged);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayCommand::redo()
|
void UndoStack::insert(qint64 pos, const QByteArray &ba)
|
||||||
{
|
{
|
||||||
switch (_cmd)
|
if ((pos >= 0) && (pos <= _chunks->size()))
|
||||||
{
|
{
|
||||||
case insert:
|
QString txt = QString(tr("Inserting %1 bytes")).arg(ba.size());
|
||||||
_xData->insert(_baPos, _newBa);
|
beginMacro(txt);
|
||||||
break;
|
for (int idx=0; idx < ba.size(); idx++)
|
||||||
case replace:
|
{
|
||||||
_oldBa = _xData->data().mid(_baPos, _len);
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos + idx, ba.at(idx));
|
||||||
_wasChanged = _xData->dataChanged(_baPos, _len);
|
this->push(cc);
|
||||||
_xData->replace(_baPos, _newBa);
|
}
|
||||||
break;
|
endMacro();
|
||||||
case remove:
|
}
|
||||||
_oldBa = _xData->data().mid(_baPos, _len);
|
}
|
||||||
_wasChanged = _xData->dataChanged(_baPos, _len);
|
|
||||||
_xData->remove(_baPos, _len);
|
void UndoStack::removeAt(qint64 pos, qint64 len)
|
||||||
break;
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
if (len==1)
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));
|
||||||
|
this->push(cc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString txt = QString(tr("Delete %1 chars")).arg(len);
|
||||||
|
beginMacro(txt);
|
||||||
|
for (qint64 cnt=0; cnt<len; cnt++)
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));
|
||||||
|
push(cc);
|
||||||
|
}
|
||||||
|
endMacro();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoStack::overwrite(qint64 pos, char c)
|
||||||
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::overwrite, pos, c);
|
||||||
|
this->push(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoStack::overwrite(qint64 pos, int len, const QByteArray &ba)
|
||||||
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
QString txt = QString(tr("Overwrite %1 chars")).arg(len);
|
||||||
|
beginMacro(txt);
|
||||||
|
removeAt(pos, len);
|
||||||
|
insert(pos, ba);
|
||||||
|
endMacro();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,66 +3,43 @@
|
|||||||
|
|
||||||
/** \cond docNever */
|
/** \cond docNever */
|
||||||
|
|
||||||
#include <QUndoCommand>
|
#include <QUndoStack>
|
||||||
|
|
||||||
#include "xbytearray.h"
|
#include "chunks.h"
|
||||||
|
|
||||||
/*! CharCommand is a class to prived undo/redo functionality in QHexEdit.
|
/*! CharCommand is a class to provid undo/redo functionality in QHexEdit.
|
||||||
A QUndoCommand represents a single editing action on a document. CharCommand
|
A QUndoCommand represents a single editing action on a document. CharCommand
|
||||||
is responsable for manipulations on single chars. It can insert. replace and
|
is responsable for manipulations on single chars. It can insert. overwrite and
|
||||||
remove characters. A manipulation stores allways to actions
|
remove characters. A manipulation stores allways two actions
|
||||||
1. redo (or do) action
|
1. redo (or do) action
|
||||||
2. undo action.
|
2. undo action.
|
||||||
|
|
||||||
CharCommand also supports command compression via mergeWidht(). This allows
|
CharCommand also supports command compression via mergeWidht(). This allows
|
||||||
the user to execute a undo command contation e.g. 3 steps in a single command.
|
the user to execute a undo command contation e.g. 3 steps in a single command.
|
||||||
If you for example insert a new byt "34" this means for the editor doing 3
|
If you for example insert a new byt "34" this means for the editor doing 3
|
||||||
steps: insert a "00", replace it with "03" and the replace it with "34". These
|
steps: insert a "00", overwrite it with "03" and the overwrite it with "34". These
|
||||||
3 steps are combined into a single step, insert a "34".
|
3 steps are combined into a single step, insert a "34".
|
||||||
|
|
||||||
|
The byte array oriented commands are just put into a set of single byte commands,
|
||||||
|
which are pooled together with the macroBegin() and macroEnd() functionality of
|
||||||
|
Qt's QUndoStack.
|
||||||
*/
|
*/
|
||||||
class CharCommand : public QUndoCommand
|
|
||||||
|
class UndoStack : public QUndoStack
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { Id = 1234 };
|
UndoStack(Chunks *chunks, QObject * parent=0);
|
||||||
enum Cmd {insert, remove, replace};
|
void insert(qint64 pos, char c);
|
||||||
|
void insert(qint64 pos, const QByteArray &ba);
|
||||||
CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar,
|
void removeAt(qint64 pos, qint64 len=1);
|
||||||
QUndoCommand *parent=0);
|
void overwrite(qint64 pos, char c);
|
||||||
|
void overwrite(qint64 pos, int len, const QByteArray &ba);
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
bool mergeWith(const QUndoCommand *command);
|
|
||||||
int id() const { return Id; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XByteArray * _xData;
|
Chunks * _chunks;
|
||||||
int _charPos;
|
QObject * _parent;
|
||||||
bool _wasChanged;
|
|
||||||
char _newChar;
|
|
||||||
char _oldChar;
|
|
||||||
Cmd _cmd;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! ArrayCommand provides undo/redo functionality for handling binary strings. It
|
|
||||||
can undo/redo insert, replace and remove binary strins (QByteArrays).
|
|
||||||
*/
|
|
||||||
class ArrayCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Cmd {insert, remove, replace};
|
|
||||||
ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0,
|
|
||||||
QUndoCommand *parent=0);
|
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Cmd _cmd;
|
|
||||||
XByteArray * _xData;
|
|
||||||
int _baPos;
|
|
||||||
int _len;
|
|
||||||
QByteArray _wasChanged;
|
|
||||||
QByteArray _newBa;
|
|
||||||
QByteArray _oldBa;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** \endcond docNever */
|
/** \endcond docNever */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,33 @@
|
|||||||
#ifndef QHEXEDIT_H
|
#ifndef QHEXEDIT_H
|
||||||
#define QHEXEDIT_H
|
#define QHEXEDIT_H
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QAbstractScrollArea>
|
||||||
#include "qhexedit_p.h"
|
#include <QPen>
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
#include "chunks.h"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
#ifdef QHEXEDIT_EXPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_EXPORT
|
||||||
|
#elif QHEXEDIT_IMPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_IMPORT
|
||||||
|
#else
|
||||||
|
#define QHEXEDIT_API
|
||||||
|
#endif
|
||||||
|
|
||||||
/** \mainpage
|
/** \mainpage
|
||||||
QHexEdit is a binary editor widget for Qt.
|
QHexEdit is a binary editor widget for Qt.
|
||||||
|
|
||||||
\version Version 0.6.1
|
\version Version 0.8.3
|
||||||
\image html hexedit.png
|
\image html qhexedit.png
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework.
|
/** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework.
|
||||||
It is a simple editor for binary data, just like QPlainTextEdit is for text
|
It is a simple editor for binary data, just like QPlainTextEdit is for text
|
||||||
data. There are sip configuration files included, so it is easy to create
|
data. There are sip configuration files included, so it is easy to create
|
||||||
bindings for PyQt and you can use this widget also in python.
|
bindings for PyQt and you can use this widget also in python 2 and 3.
|
||||||
|
|
||||||
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
|
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
|
||||||
the mouse or the keyboard to navigate inside the widget. If you hit the keys
|
the mouse or the keyboard to navigate inside the widget. If you hit the keys
|
||||||
@ -36,44 +48,78 @@ characters will be ignored.
|
|||||||
QHexEdit comes with undo/redo functionality. All changes can be undone, by
|
QHexEdit comes with undo/redo functionality. All changes can be undone, by
|
||||||
pressing the undo-key (usually ctr-z). They can also be redone afterwards.
|
pressing the undo-key (usually ctr-z). They can also be redone afterwards.
|
||||||
The undo/redo framework is cleared, when setData() sets up a new
|
The undo/redo framework is cleared, when setData() sets up a new
|
||||||
content for the editor.
|
content for the editor. You can search data inside the content with indexOf()
|
||||||
|
and lastIndexOf(). The replace() function is to change located subdata. This
|
||||||
|
'replaced' data can also be undone by the undo/redo framework.
|
||||||
|
|
||||||
This widget can only handle small amounts of data. The size has to be below 10
|
QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of
|
||||||
megabytes, otherwise the scroll sliders ard not shown and you can't scroll any
|
data. The size of edited data can be more then two gigabytes without any
|
||||||
more.
|
restrictions.
|
||||||
*/
|
*/
|
||||||
class QHexEdit : public QScrollArea
|
class QHEXEDIT_API QHexEdit : public QAbstractScrollArea
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
/*! Property data holds the content of QHexEdit. Call setData() to set the
|
|
||||||
content of QHexEdit, data() returns the actual content.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(QByteArray data READ data WRITE setData)
|
|
||||||
|
|
||||||
/*! Property addressOffset is added to the Numbers of the Address Area.
|
/*! Property address area switch the address area on or off. Set addressArea true
|
||||||
A offset in the address area (left side) is sometimes usefull, whe you show
|
(show it), false (hide it).
|
||||||
only a segment of a complete memory picture. With setAddressOffset() you set
|
|
||||||
this property - with addressOffset() you get the actual value.
|
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset)
|
Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea)
|
||||||
|
|
||||||
/*! Property address area color sets (setAddressAreaColor()) the backgorund
|
/*! Property address area color sets (setAddressAreaColor()) the backgorund
|
||||||
color of address areas. You can also read the color (addressaAreaColor()).
|
color of address areas. You can also read the color (addressaAreaColor()).
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)
|
Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)
|
||||||
|
|
||||||
|
/*! Property addressOffset is added to the Numbers of the Address Area.
|
||||||
|
A offset in the address area (left side) is sometimes usefull, whe you show
|
||||||
|
only a segment of a complete memory picture. With setAddressOffset() you set
|
||||||
|
this property - with addressOffset() you get the current value.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset)
|
||||||
|
|
||||||
|
/*! Set and get the minimum width of the address area, width in characters.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth)
|
||||||
|
|
||||||
|
/*! Switch the ascii area on (true, show it) or off (false, hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea)
|
||||||
|
|
||||||
|
/*! Set and get bytes number per line.*/
|
||||||
|
Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine)
|
||||||
|
|
||||||
|
/*! Porperty cursorPosition sets or gets the position of the editor cursor
|
||||||
|
in QHexEdit. Every byte in data has to cursor positions: the lower and upper
|
||||||
|
Nibble. Maximum cursor position is factor two of data.size().
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition)
|
||||||
|
|
||||||
|
/*! Property data holds the content of QHexEdit. Call setData() to set the
|
||||||
|
content of QHexEdit, data() returns the actual content. When calling setData()
|
||||||
|
with a QByteArray as argument, QHexEdit creates a internal copy of the data
|
||||||
|
If you want to edit big files please use setData(), based on QIODevice.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged)
|
||||||
|
|
||||||
|
/*! That property defines if the hex values looks as a-f if the value is false(default)
|
||||||
|
or A-F if value is true.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool hexCaps READ hexCaps WRITE setHexCaps)
|
||||||
|
|
||||||
|
/*! Property defines the dynamic calculation of bytesPerLine parameter depends of width of widget.
|
||||||
|
set this property true to avoid horizontal scrollbars and show the maximal possible data. defalut value is false*/
|
||||||
|
Q_PROPERTY(bool dynamicBytesPerLine READ dynamicBytesPerLine WRITE setDynamicBytesPerLine)
|
||||||
|
|
||||||
|
/*! Switch the highlighting feature on or of: true (show it), false (hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting)
|
||||||
|
|
||||||
/*! Property highlighting color sets (setHighlightingColor()) the backgorund
|
/*! Property highlighting color sets (setHighlightingColor()) the backgorund
|
||||||
color of highlighted text areas. You can also read the color
|
color of highlighted text areas. You can also read the color
|
||||||
(highlightingColor()).
|
(highlightingColor()).
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)
|
Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)
|
||||||
|
|
||||||
/*! Property selection color sets (setSelectionColor()) the backgorund
|
|
||||||
color of selected text areas. You can also read the color
|
|
||||||
(selectionColor()).
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor)
|
|
||||||
|
|
||||||
/*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode
|
/*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode
|
||||||
in which the editor works. In overwrite mode the user will overwrite existing data. The
|
in which the editor works. In overwrite mode the user will overwrite existing data. The
|
||||||
size of data will be constant. In insert mode the size will grow, when inserting
|
size of data will be constant. In insert mode the size will grow, when inserting
|
||||||
@ -81,6 +127,12 @@ more.
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
|
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
|
||||||
|
|
||||||
|
/*! Property selection color sets (setSelectionColor()) the backgorund
|
||||||
|
color of selected text areas. You can also read the color
|
||||||
|
(selectionColor()).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor)
|
||||||
|
|
||||||
/*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
|
/*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
|
||||||
in which the editor works. In readonly mode the the user can only navigate
|
in which the editor works. In readonly mode the the user can only navigate
|
||||||
through the data and select data; modifying is not possible. This
|
through the data and select data; modifying is not possible. This
|
||||||
@ -91,62 +143,115 @@ more.
|
|||||||
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
|
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
|
||||||
Q_PROPERTY(QFont font READ font WRITE setFont)
|
Q_PROPERTY(QFont font READ font WRITE setFont)
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*! Creates an instance of QHexEdit.
|
/*! Creates an instance of QHexEdit.
|
||||||
\param parent Parent widget of QHexEdit.
|
\param parent Parent widget of QHexEdit.
|
||||||
*/
|
*/
|
||||||
QHexEdit(QWidget *parent=0);
|
QHexEdit(QWidget *parent=0);
|
||||||
|
|
||||||
/*! Inserts a byte array.
|
// Access to data of qhexedit
|
||||||
\param i Index position, where to insert
|
|
||||||
\param ba byte array, which is to insert
|
/*! Sets the data of QHexEdit. The QIODevice will be opend just before reading
|
||||||
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
|
and closed immediately afterwards. This is to allow other programs to rewrite
|
||||||
insertet and size of data grows.
|
the file while editing it.
|
||||||
*/
|
*/
|
||||||
void insert(int i, const QByteArray & ba);
|
bool setData(QIODevice &iODevice);
|
||||||
|
|
||||||
|
/*! Givs back the data as a QByteArray starting at position \param pos and
|
||||||
|
delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
QByteArray dataAt(qint64 pos, qint64 count=-1);
|
||||||
|
|
||||||
|
/*! Givs back the data into a \param iODevice starting at position \param pos
|
||||||
|
and delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
|
||||||
|
// Char handling
|
||||||
|
|
||||||
/*! Inserts a char.
|
/*! Inserts a char.
|
||||||
\param i Index position, where to insert
|
\param pos Index position, where to insert
|
||||||
\param ch Char, which is to insert
|
\param ch Char, which is to insert
|
||||||
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
|
The char will be inserted and size of data grows.
|
||||||
insertet and size of data grows.
|
|
||||||
*/
|
*/
|
||||||
void insert(int i, char ch);
|
void insert(qint64 pos, char ch);
|
||||||
|
|
||||||
/*! Removes len bytes from the content.
|
/*! Removes len bytes from the content.
|
||||||
\param pos Index position, where to remove
|
\param pos Index position, where to remove
|
||||||
\param len Amount of bytes to remove
|
\param len Amount of bytes to remove
|
||||||
In overwrite mode, the existing bytes will be overwriten with 0x00.
|
|
||||||
*/
|
*/
|
||||||
void remove(int pos, int len=1);
|
void remove(qint64 pos, qint64 len=1);
|
||||||
|
|
||||||
/*! Gives back a formatted image of the content of QHexEdit
|
/*! Replaces a char.
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ch Char, which is to insert
|
||||||
|
The char will be overwritten and size remains constant.
|
||||||
*/
|
*/
|
||||||
QString toReadableString();
|
void replace(qint64 pos, char ch);
|
||||||
|
|
||||||
|
|
||||||
|
// ByteArray handling
|
||||||
|
|
||||||
|
/*! Inserts a byte array.
|
||||||
|
\param pos Index position, where to insert
|
||||||
|
\param ba QByteArray, which is to insert
|
||||||
|
The QByteArray will be inserted and size of data grows.
|
||||||
|
*/
|
||||||
|
void insert(qint64 pos, const QByteArray &ba);
|
||||||
|
|
||||||
|
/*! Replaces \param len bytes with a byte array \param ba
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ba QByteArray, which is inserted
|
||||||
|
\param len count of bytes to overwrite
|
||||||
|
The data is overwritten and size of data may change.
|
||||||
|
*/
|
||||||
|
void replace(qint64 pos, qint64 len, const QByteArray &ba);
|
||||||
|
|
||||||
|
|
||||||
|
// Utility functioins
|
||||||
|
/*! Calc cursor position from graphics position
|
||||||
|
* \param point from where the cursor position should be calculated
|
||||||
|
* \return Cursor postioin
|
||||||
|
*/
|
||||||
|
qint64 cursorPosition(QPoint point);
|
||||||
|
|
||||||
|
/*! Ensure the cursor to be visble
|
||||||
|
*/
|
||||||
|
void ensureVisible();
|
||||||
|
|
||||||
|
/*! Find first occurence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
/*! Returns if any changes where done on document
|
||||||
|
* \return true when document is modified else false
|
||||||
|
*/
|
||||||
|
bool isModified();
|
||||||
|
|
||||||
|
/*! Find last occurence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
/*! Gives back a formatted image of the selected content of QHexEdit
|
/*! Gives back a formatted image of the selected content of QHexEdit
|
||||||
*/
|
*/
|
||||||
QString selectionToReadableString();
|
QString selectionToReadableString();
|
||||||
|
|
||||||
/*! \cond docNever */
|
/*! Set Font of QHexEdit
|
||||||
void setAddressOffset(int offset);
|
* \param font
|
||||||
int addressOffset();
|
*/
|
||||||
void setData(QByteArray const &data);
|
void setFont(const QFont &font);
|
||||||
QByteArray data();
|
|
||||||
void setAddressAreaColor(QColor const &color);
|
/*! Gives back a formatted image of the content of QHexEdit
|
||||||
QColor addressAreaColor();
|
*/
|
||||||
void setHighlightingColor(QColor const &color);
|
QString toReadableString();
|
||||||
QColor highlightingColor();
|
|
||||||
void setSelectionColor(QColor const &color);
|
|
||||||
QColor selectionColor();
|
|
||||||
void setOverwriteMode(bool);
|
|
||||||
bool overwriteMode();
|
|
||||||
void setReadOnly(bool);
|
|
||||||
bool isReadOnly();
|
|
||||||
const QFont &font() const;
|
|
||||||
void setFont(const QFont &);
|
|
||||||
/*! \endcond docNever */
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/*! Redoes the last operation. If there is no operation to redo, i.e.
|
/*! Redoes the last operation. If there is no operation to redo, i.e.
|
||||||
@ -154,26 +259,6 @@ public slots:
|
|||||||
*/
|
*/
|
||||||
void redo();
|
void redo();
|
||||||
|
|
||||||
/*! Set the minimum width of the address area.
|
|
||||||
\param addressWidth Width in characters.
|
|
||||||
*/
|
|
||||||
void setAddressWidth(int addressWidth);
|
|
||||||
|
|
||||||
/*! Switch the address area on or off.
|
|
||||||
\param addressArea true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setAddressArea(bool addressArea);
|
|
||||||
|
|
||||||
/*! Switch the ascii area on or off.
|
|
||||||
\param asciiArea true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setAsciiArea(bool asciiArea);
|
|
||||||
|
|
||||||
/*! Switch the highlighting feature on or of.
|
|
||||||
\param mode true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setHighlighting(bool mode);
|
|
||||||
|
|
||||||
/*! Undoes the last operation. If there is no operation to undo, i.e.
|
/*! Undoes the last operation. If there is no operation to undo, i.e.
|
||||||
there is no undo step in the undo/redo history, nothing happens.
|
there is no undo step in the undo/redo history, nothing happens.
|
||||||
*/
|
*/
|
||||||
@ -182,24 +267,153 @@ public slots:
|
|||||||
signals:
|
signals:
|
||||||
|
|
||||||
/*! Contains the address, where the cursor is located. */
|
/*! Contains the address, where the cursor is located. */
|
||||||
void currentAddressChanged(int address);
|
void currentAddressChanged(qint64 address);
|
||||||
|
|
||||||
/*! Contains the size of the data to edit. */
|
/*! Contains the size of the data to edit. */
|
||||||
void currentSizeChanged(int size);
|
void currentSizeChanged(qint64 size);
|
||||||
|
|
||||||
/*! The signal is emited every time, the data is changed. */
|
/*! The signal is emitted every time, the data is changed. */
|
||||||
void dataChanged();
|
void dataChanged();
|
||||||
|
|
||||||
/*! The signal is emited every time, the overwrite mode is changed. */
|
/*! The signal is emitted every time, the overwrite mode is changed. */
|
||||||
void overwriteModeChanged(bool state);
|
void overwriteModeChanged(bool state);
|
||||||
|
|
||||||
private:
|
|
||||||
/*! \cond docNever */
|
/*! \cond docNever */
|
||||||
QHexEditPrivate *qHexEdit_p;
|
public:
|
||||||
QHBoxLayout *layout;
|
~QHexEdit();
|
||||||
QScrollArea *scrollArea;
|
|
||||||
|
// Properties
|
||||||
|
bool addressArea();
|
||||||
|
void setAddressArea(bool addressArea);
|
||||||
|
|
||||||
|
QColor addressAreaColor();
|
||||||
|
void setAddressAreaColor(const QColor &color);
|
||||||
|
|
||||||
|
qint64 addressOffset();
|
||||||
|
void setAddressOffset(qint64 addressArea);
|
||||||
|
|
||||||
|
int addressWidth();
|
||||||
|
void setAddressWidth(int addressWidth);
|
||||||
|
|
||||||
|
bool asciiArea();
|
||||||
|
void setAsciiArea(bool asciiArea);
|
||||||
|
|
||||||
|
int bytesPerLine();
|
||||||
|
void setBytesPerLine(int count);
|
||||||
|
|
||||||
|
qint64 cursorPosition();
|
||||||
|
void setCursorPosition(qint64 position);
|
||||||
|
|
||||||
|
QByteArray data();
|
||||||
|
void setData(const QByteArray &ba);
|
||||||
|
|
||||||
|
void setHexCaps(const bool isCaps);
|
||||||
|
bool hexCaps();
|
||||||
|
|
||||||
|
void setDynamicBytesPerLine(const bool isDynamic);
|
||||||
|
bool dynamicBytesPerLine();
|
||||||
|
|
||||||
|
bool highlighting();
|
||||||
|
void setHighlighting(bool mode);
|
||||||
|
|
||||||
|
QColor highlightingColor();
|
||||||
|
void setHighlightingColor(const QColor &color);
|
||||||
|
|
||||||
|
bool overwriteMode();
|
||||||
|
void setOverwriteMode(bool overwriteMode);
|
||||||
|
|
||||||
|
bool isReadOnly();
|
||||||
|
void setReadOnly(bool readOnly);
|
||||||
|
|
||||||
|
QColor selectionColor();
|
||||||
|
void setSelectionColor(const QColor &color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Handle events
|
||||||
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent * event);
|
||||||
|
void mousePressEvent(QMouseEvent * event);
|
||||||
|
void paintEvent(QPaintEvent *event);
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
virtual bool focusNextPrevChild(bool next);
|
||||||
|
private:
|
||||||
|
// Handle selections
|
||||||
|
void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos
|
||||||
|
void resetSelection(); // set selectionEnd to selectionStart
|
||||||
|
void setSelection(qint64 pos); // set min (if below init) or max (if greater init)
|
||||||
|
int getSelectionBegin();
|
||||||
|
int getSelectionEnd();
|
||||||
|
|
||||||
|
// Private utility functions
|
||||||
|
void init();
|
||||||
|
void readBuffers();
|
||||||
|
QString toReadable(const QByteArray &ba);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void adjust(); // recalc pixel positions
|
||||||
|
void dataChangedPrivate(int idx=0); // emit dataChanged() signal
|
||||||
|
void refresh(); // ensureVisible() and readBuffers()
|
||||||
|
void updateCursor(); // update blinking cursor
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Name convention: pixel positions start with _px
|
||||||
|
int _pxCharWidth, _pxCharHeight; // char dimensions (dpendend on font)
|
||||||
|
int _pxPosHexX; // X-Pos of HeaxArea
|
||||||
|
int _pxPosAdrX; // X-Pos of Address Area
|
||||||
|
int _pxPosAsciiX; // X-Pos of Ascii Area
|
||||||
|
int _pxGapAdr; // gap left from AddressArea
|
||||||
|
int _pxGapAdrHex; // gap between AddressArea and HexAerea
|
||||||
|
int _pxGapHexAscii; // gap between HexArea and AsciiArea
|
||||||
|
int _pxCursorWidth; // cursor width
|
||||||
|
int _pxSelectionSub; // offset selection rect
|
||||||
|
int _pxCursorX; // current cursor pos
|
||||||
|
int _pxCursorY; // current cursor pos
|
||||||
|
|
||||||
|
// Name convention: absolute byte positions in chunks start with _b
|
||||||
|
qint64 _bSelectionBegin; // first position of Selection
|
||||||
|
qint64 _bSelectionEnd; // end of Selection
|
||||||
|
qint64 _bSelectionInit; // memory position of Selection
|
||||||
|
qint64 _bPosFirst; // position of first byte shown
|
||||||
|
qint64 _bPosLast; // position of last byte shown
|
||||||
|
qint64 _bPosCurrent; // current position
|
||||||
|
|
||||||
|
// variables to store the property values
|
||||||
|
bool _addressArea; // left area of QHexEdit
|
||||||
|
QColor _addressAreaColor;
|
||||||
|
int _addressWidth;
|
||||||
|
bool _asciiArea;
|
||||||
|
qint64 _addressOffset;
|
||||||
|
int _bytesPerLine;
|
||||||
|
int _hexCharsInLine;
|
||||||
|
bool _highlighting;
|
||||||
|
bool _overwriteMode;
|
||||||
|
QBrush _brushSelection;
|
||||||
|
QPen _penSelection;
|
||||||
|
QBrush _brushHighlighted;
|
||||||
|
QPen _penHighlighted;
|
||||||
|
bool _readOnly;
|
||||||
|
bool _hexCaps;
|
||||||
|
bool _dynamicBytesPerLine;
|
||||||
|
|
||||||
|
// other variables
|
||||||
|
bool _editAreaIsAscii; // flag about the ascii mode edited
|
||||||
|
int _addrDigits; // real no of addressdigits, may be > addressWidth
|
||||||
|
bool _blink; // help get cursor blinking
|
||||||
|
QBuffer _bData; // buffer, when setup with QByteArray
|
||||||
|
Chunks *_chunks; // IODevice based access to data
|
||||||
|
QTimer _cursorTimer; // for blinking cursor
|
||||||
|
qint64 _cursorPosition; // absolute positioin of cursor, 1 Byte == 2 tics
|
||||||
|
QRect _cursorRect; // physical dimensions of cursor
|
||||||
|
QByteArray _data; // QHexEdit's data, when setup with QByteArray
|
||||||
|
QByteArray _dataShown; // data in the current View
|
||||||
|
QByteArray _hexDataShown; // data in view, transformed to hex
|
||||||
|
qint64 _lastEventSize; // size, which was emitted last time
|
||||||
|
QByteArray _markedShown; // marked data in view
|
||||||
|
bool _modified; // Is any data in editor modified?
|
||||||
|
int _rowsShown; // lines of text shown
|
||||||
|
UndoStack * _undoStack; // Stack to store edit actions for undo/redo
|
||||||
/*! \endcond docNever */
|
/*! \endcond docNever */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // QHEXEDIT_H
|
||||||
|
|
||||||
|
@ -1,800 +0,0 @@
|
|||||||
#include <QtGui>
|
|
||||||
|
|
||||||
#include "qhexedit_p.h"
|
|
||||||
#include "commands.h"
|
|
||||||
|
|
||||||
const int HEXCHARS_IN_LINE = 47;
|
|
||||||
const int GAP_ADR_HEX = 10;
|
|
||||||
const int GAP_HEX_ASCII = 16;
|
|
||||||
const int BYTES_PER_LINE = 16;
|
|
||||||
|
|
||||||
QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent)
|
|
||||||
{
|
|
||||||
_undoStack = new QUndoStack(this);
|
|
||||||
|
|
||||||
_scrollArea = parent;
|
|
||||||
setAddressWidth(4);
|
|
||||||
setAddressOffset(0);
|
|
||||||
setAddressArea(true);
|
|
||||||
setAsciiArea(true);
|
|
||||||
setHighlighting(true);
|
|
||||||
setOverwriteMode(true);
|
|
||||||
setReadOnly(false);
|
|
||||||
setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff));
|
|
||||||
setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));
|
|
||||||
setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff));
|
|
||||||
setFont(QFont("Courier", 10));
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
resetSelection(0);
|
|
||||||
|
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
|
||||||
|
|
||||||
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
|
|
||||||
_cursorTimer.setInterval(500);
|
|
||||||
_cursorTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setAddressOffset(int offset)
|
|
||||||
{
|
|
||||||
_xData.setAddressOffset(offset);
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHexEditPrivate::addressOffset()
|
|
||||||
{
|
|
||||||
return _xData.addressOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setData(const QByteArray &data)
|
|
||||||
{
|
|
||||||
_xData.setData(data);
|
|
||||||
_undoStack->clear();
|
|
||||||
adjust();
|
|
||||||
setCursorPos(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray QHexEditPrivate::data()
|
|
||||||
{
|
|
||||||
return _xData.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setAddressAreaColor(const QColor &color)
|
|
||||||
{
|
|
||||||
_addressAreaColor = color;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor QHexEditPrivate::addressAreaColor()
|
|
||||||
{
|
|
||||||
return _addressAreaColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setHighlightingColor(const QColor &color)
|
|
||||||
{
|
|
||||||
_highlightingColor = color;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor QHexEditPrivate::highlightingColor()
|
|
||||||
{
|
|
||||||
return _highlightingColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setSelectionColor(const QColor &color)
|
|
||||||
{
|
|
||||||
_selectionColor = color;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor QHexEditPrivate::selectionColor()
|
|
||||||
{
|
|
||||||
return _selectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setReadOnly(bool readOnly)
|
|
||||||
{
|
|
||||||
_readOnly = readOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QHexEditPrivate::isReadOnly()
|
|
||||||
{
|
|
||||||
return _readOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
XByteArray & QHexEditPrivate::xData()
|
|
||||||
{
|
|
||||||
return _xData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::insert(int index, const QByteArray & ba)
|
|
||||||
{
|
|
||||||
if (ba.length() > 0)
|
|
||||||
{
|
|
||||||
if (_overwriteMode)
|
|
||||||
{
|
|
||||||
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
||||||
_undoStack->push(arrayCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length());
|
|
||||||
_undoStack->push(arrayCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::insert(int index, char ch)
|
|
||||||
{
|
|
||||||
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch);
|
|
||||||
_undoStack->push(charCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::remove(int index, int len)
|
|
||||||
{
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
if (len == 1)
|
|
||||||
{
|
|
||||||
if (_overwriteMode)
|
|
||||||
{
|
|
||||||
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0));
|
|
||||||
_undoStack->push(charCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0));
|
|
||||||
_undoStack->push(charCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QByteArray ba = QByteArray(len, char(0));
|
|
||||||
if (_overwriteMode)
|
|
||||||
{
|
|
||||||
QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
||||||
_undoStack->push(arrayCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len);
|
|
||||||
_undoStack->push(arrayCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::replace(int index, char ch)
|
|
||||||
{
|
|
||||||
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch);
|
|
||||||
_undoStack->push(charCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::replace(int index, const QByteArray & ba)
|
|
||||||
{
|
|
||||||
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
||||||
_undoStack->push(arrayCommand);
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setAddressArea(bool addressArea)
|
|
||||||
{
|
|
||||||
_addressArea = addressArea;
|
|
||||||
adjust();
|
|
||||||
|
|
||||||
setCursorPos(_cursorPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setAddressWidth(int addressWidth)
|
|
||||||
{
|
|
||||||
_xData.setAddressWidth(addressWidth);
|
|
||||||
|
|
||||||
setCursorPos(_cursorPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setAsciiArea(bool asciiArea)
|
|
||||||
{
|
|
||||||
_asciiArea = asciiArea;
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setFont(const QFont &font)
|
|
||||||
{
|
|
||||||
QWidget::setFont(font);
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setHighlighting(bool mode)
|
|
||||||
{
|
|
||||||
_highlighting = mode;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setOverwriteMode(bool overwriteMode)
|
|
||||||
{
|
|
||||||
_overwriteMode = overwriteMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QHexEditPrivate::overwriteMode()
|
|
||||||
{
|
|
||||||
return _overwriteMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::redo()
|
|
||||||
{
|
|
||||||
_undoStack->redo();
|
|
||||||
emit dataChanged();
|
|
||||||
setCursorPos(_cursorPosition);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::undo()
|
|
||||||
{
|
|
||||||
_undoStack->undo();
|
|
||||||
emit dataChanged();
|
|
||||||
setCursorPos(_cursorPosition);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QHexEditPrivate::toRedableString()
|
|
||||||
{
|
|
||||||
return _xData.toRedableString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString QHexEditPrivate::selectionToReadableString()
|
|
||||||
{
|
|
||||||
return _xData.toRedableString(getSelectionBegin(), getSelectionEnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::keyPressEvent(QKeyEvent *event)
|
|
||||||
{
|
|
||||||
int charX = (_cursorX - _xPosHex) / _charWidth;
|
|
||||||
int posX = (charX / 3) * 2 + (charX % 3);
|
|
||||||
int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Cursor movements */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
if (event->matches(QKeySequence::MoveToNextChar))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition + 1);
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToPreviousChar))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition - 1);
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToEndOfLine))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToStartOfLine))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToPreviousLine))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToNextLine))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->matches(QKeySequence::MoveToNextPage))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToPreviousPage))
|
|
||||||
{
|
|
||||||
setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToEndOfDocument))
|
|
||||||
{
|
|
||||||
setCursorPos(_xData.size() * 2);
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::MoveToStartOfDocument))
|
|
||||||
{
|
|
||||||
setCursorPos(0);
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Select commands */
|
|
||||||
/*****************************************************************************/
|
|
||||||
if (event->matches(QKeySequence::SelectAll))
|
|
||||||
{
|
|
||||||
resetSelection(0);
|
|
||||||
setSelection(2*_xData.size() + 1);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectNextChar))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition + 1;
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectPreviousChar))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition - 1;
|
|
||||||
setSelection(pos);
|
|
||||||
setCursorPos(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectEndOfLine))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE);
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectStartOfLine))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE));
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectPreviousLine))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition - (2 * BYTES_PER_LINE);
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectNextLine))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition + (2 * BYTES_PER_LINE);
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->matches(QKeySequence::SelectNextPage))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectPreviousPage))
|
|
||||||
{
|
|
||||||
int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectEndOfDocument))
|
|
||||||
{
|
|
||||||
int pos = _xData.size() * 2;
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
if (event->matches(QKeySequence::SelectStartOfDocument))
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
setCursorPos(pos);
|
|
||||||
setSelection(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Edit Commands */
|
|
||||||
/*****************************************************************************/
|
|
||||||
if (!_readOnly)
|
|
||||||
{
|
|
||||||
/* Hex input */
|
|
||||||
int key = int(event->text()[0].toAscii());
|
|
||||||
if ((key>='0' && key<='9') || (key>='a' && key <= 'f'))
|
|
||||||
{
|
|
||||||
if (getSelectionBegin() != getSelectionEnd())
|
|
||||||
{
|
|
||||||
posBa = getSelectionBegin();
|
|
||||||
remove(posBa, getSelectionEnd() - posBa);
|
|
||||||
setCursorPos(2*posBa);
|
|
||||||
resetSelection(2*posBa);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If insert mode, then insert a byte
|
|
||||||
if (_overwriteMode == false)
|
|
||||||
if ((charX % 3) == 0)
|
|
||||||
{
|
|
||||||
insert(posBa, char(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change content
|
|
||||||
if (_xData.size() > 0)
|
|
||||||
{
|
|
||||||
QByteArray hexValue = _xData.data().mid(posBa, 1).toHex();
|
|
||||||
if ((charX % 3) == 0)
|
|
||||||
hexValue[0] = key;
|
|
||||||
else
|
|
||||||
hexValue[1] = key;
|
|
||||||
|
|
||||||
replace(posBa, QByteArray().fromHex(hexValue)[0]);
|
|
||||||
|
|
||||||
setCursorPos(_cursorPosition + 1);
|
|
||||||
resetSelection(_cursorPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cut & Paste */
|
|
||||||
if (event->matches(QKeySequence::Cut))
|
|
||||||
{
|
|
||||||
QString result = QString();
|
|
||||||
for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++)
|
|
||||||
{
|
|
||||||
result += _xData.data().mid(idx, 1).toHex() + " ";
|
|
||||||
if ((idx % 16) == 15)
|
|
||||||
result.append("\n");
|
|
||||||
}
|
|
||||||
remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
clipboard->setText(result);
|
|
||||||
setCursorPos(getSelectionBegin());
|
|
||||||
resetSelection(getSelectionBegin());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->matches(QKeySequence::Paste))
|
|
||||||
{
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());
|
|
||||||
insert(_cursorPosition / 2, ba);
|
|
||||||
setCursorPos(_cursorPosition + 2 * ba.length());
|
|
||||||
resetSelection(getSelectionBegin());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Delete char */
|
|
||||||
if (event->matches(QKeySequence::Delete))
|
|
||||||
{
|
|
||||||
if (getSelectionBegin() != getSelectionEnd())
|
|
||||||
{
|
|
||||||
posBa = getSelectionBegin();
|
|
||||||
remove(posBa, getSelectionEnd() - posBa);
|
|
||||||
setCursorPos(2*posBa);
|
|
||||||
resetSelection(2*posBa);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_overwriteMode)
|
|
||||||
replace(posBa, char(0));
|
|
||||||
else
|
|
||||||
remove(posBa, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Backspace */
|
|
||||||
if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier))
|
|
||||||
{
|
|
||||||
if (getSelectionBegin() != getSelectionEnd())
|
|
||||||
{
|
|
||||||
posBa = getSelectionBegin();
|
|
||||||
remove(posBa, getSelectionEnd() - posBa);
|
|
||||||
setCursorPos(2*posBa);
|
|
||||||
resetSelection(2*posBa);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (posBa > 0)
|
|
||||||
{
|
|
||||||
if (_overwriteMode)
|
|
||||||
replace(posBa - 1, char(0));
|
|
||||||
else
|
|
||||||
remove(posBa - 1, 1);
|
|
||||||
setCursorPos(_cursorPosition - 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* undo */
|
|
||||||
if (event->matches(QKeySequence::Undo))
|
|
||||||
{
|
|
||||||
undo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* redo */
|
|
||||||
if (event->matches(QKeySequence::Redo))
|
|
||||||
{
|
|
||||||
redo();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->matches(QKeySequence::Copy))
|
|
||||||
{
|
|
||||||
QString result = QString();
|
|
||||||
for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++)
|
|
||||||
{
|
|
||||||
result += _xData.data().mid(idx, 1).toHex() + " ";
|
|
||||||
if ((idx % 16) == 15)
|
|
||||||
result.append('\n');
|
|
||||||
}
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
clipboard->setText(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch between insert/overwrite mode
|
|
||||||
if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier))
|
|
||||||
{
|
|
||||||
_overwriteMode = !_overwriteMode;
|
|
||||||
setCursorPos(_cursorPosition);
|
|
||||||
overwriteModeChanged(_overwriteMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event)
|
|
||||||
{
|
|
||||||
_blink = false;
|
|
||||||
update();
|
|
||||||
int actPos = cursorPos(event->pos());
|
|
||||||
setCursorPos(actPos);
|
|
||||||
setSelection(actPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::mousePressEvent(QMouseEvent * event)
|
|
||||||
{
|
|
||||||
_blink = false;
|
|
||||||
update();
|
|
||||||
int cPos = cursorPos(event->pos());
|
|
||||||
resetSelection(cPos);
|
|
||||||
setCursorPos(cPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::paintEvent(QPaintEvent *event)
|
|
||||||
{
|
|
||||||
QPainter painter(this);
|
|
||||||
|
|
||||||
// draw some patterns if needed
|
|
||||||
painter.fillRect(event->rect(), this->palette().color(QPalette::Base));
|
|
||||||
if (_addressArea)
|
|
||||||
painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor);
|
|
||||||
if (_asciiArea)
|
|
||||||
{
|
|
||||||
int linePos = _xPosAscii - (GAP_HEX_ASCII / 2);
|
|
||||||
painter.setPen(Qt::gray);
|
|
||||||
painter.drawLine(linePos, event->rect().top(), linePos, height());
|
|
||||||
}
|
|
||||||
|
|
||||||
painter.setPen(this->palette().color(QPalette::WindowText));
|
|
||||||
|
|
||||||
// calc position
|
|
||||||
int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE;
|
|
||||||
if (firstLineIdx < 0)
|
|
||||||
firstLineIdx = 0;
|
|
||||||
int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE;
|
|
||||||
if (lastLineIdx > _xData.size())
|
|
||||||
lastLineIdx = _xData.size();
|
|
||||||
int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight;
|
|
||||||
|
|
||||||
// paint address area
|
|
||||||
if (_addressArea)
|
|
||||||
{
|
|
||||||
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
||||||
{
|
|
||||||
QString address = QString("%1")
|
|
||||||
.arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0'));
|
|
||||||
painter.drawText(_xPosAdr, yPos, address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint hex area
|
|
||||||
QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex());
|
|
||||||
QBrush highLighted = QBrush(_highlightingColor);
|
|
||||||
QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText));
|
|
||||||
QBrush selected = QBrush(_selectionColor);
|
|
||||||
QPen colSelected = QPen(Qt::white);
|
|
||||||
QPen colStandard = QPen(this->palette().color(QPalette::WindowText));
|
|
||||||
|
|
||||||
painter.setBackgroundMode(Qt::TransparentMode);
|
|
||||||
|
|
||||||
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
||||||
{
|
|
||||||
QByteArray hex;
|
|
||||||
int xPos = _xPosHex;
|
|
||||||
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
||||||
{
|
|
||||||
int posBa = lineIdx + colIdx;
|
|
||||||
if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
|
|
||||||
{
|
|
||||||
painter.setBackground(selected);
|
|
||||||
painter.setBackgroundMode(Qt::OpaqueMode);
|
|
||||||
painter.setPen(colSelected);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_highlighting)
|
|
||||||
{
|
|
||||||
// hilight diff bytes
|
|
||||||
painter.setBackground(highLighted);
|
|
||||||
if (_xData.dataChanged(posBa))
|
|
||||||
{
|
|
||||||
painter.setPen(colHighlighted);
|
|
||||||
painter.setBackgroundMode(Qt::OpaqueMode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
painter.setPen(colStandard);
|
|
||||||
painter.setBackgroundMode(Qt::TransparentMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render hex value
|
|
||||||
if (colIdx == 0)
|
|
||||||
{
|
|
||||||
hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2);
|
|
||||||
painter.drawText(xPos, yPos, hex);
|
|
||||||
xPos += 2 * _charWidth;
|
|
||||||
} else {
|
|
||||||
hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" ");
|
|
||||||
painter.drawText(xPos, yPos, hex);
|
|
||||||
xPos += 3 * _charWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
painter.setBackgroundMode(Qt::TransparentMode);
|
|
||||||
painter.setPen(this->palette().color(QPalette::WindowText));
|
|
||||||
|
|
||||||
// paint ascii area
|
|
||||||
if (_asciiArea)
|
|
||||||
{
|
|
||||||
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
||||||
{
|
|
||||||
int xPosAscii = _xPosAscii;
|
|
||||||
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
||||||
{
|
|
||||||
painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx));
|
|
||||||
xPosAscii += _charWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint cursor
|
|
||||||
if (_blink)
|
|
||||||
{
|
|
||||||
if (_overwriteMode)
|
|
||||||
painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText));
|
|
||||||
else
|
|
||||||
painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_size != _xData.size())
|
|
||||||
{
|
|
||||||
_size = _xData.size();
|
|
||||||
emit currentSizeChanged(_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setCursorPos(int position)
|
|
||||||
{
|
|
||||||
// delete cursor
|
|
||||||
_blink = false;
|
|
||||||
update();
|
|
||||||
|
|
||||||
// cursor in range?
|
|
||||||
if (_overwriteMode)
|
|
||||||
{
|
|
||||||
if (position > (_xData.size() * 2 - 1))
|
|
||||||
position = _xData.size() * 2 - 1;
|
|
||||||
} else {
|
|
||||||
if (position > (_xData.size() * 2))
|
|
||||||
position = _xData.size() * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position < 0)
|
|
||||||
position = 0;
|
|
||||||
|
|
||||||
// calc position
|
|
||||||
_cursorPosition = position;
|
|
||||||
_cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4;
|
|
||||||
int x = (position % (2 * BYTES_PER_LINE));
|
|
||||||
_cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex;
|
|
||||||
|
|
||||||
// immiadately draw cursor
|
|
||||||
_blink = true;
|
|
||||||
update();
|
|
||||||
emit currentAddressChanged(_cursorPosition/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHexEditPrivate::cursorPos(QPoint pos)
|
|
||||||
{
|
|
||||||
int result = -1;
|
|
||||||
// find char under cursor
|
|
||||||
if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth)))
|
|
||||||
{
|
|
||||||
int x = (pos.x() - _xPosHex) / _charWidth;
|
|
||||||
if ((x % 3) == 0)
|
|
||||||
x = (x / 3) * 2;
|
|
||||||
else
|
|
||||||
x = ((x / 3) * 2) + 1;
|
|
||||||
int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE;
|
|
||||||
result = x + y;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHexEditPrivate::cursorPos()
|
|
||||||
{
|
|
||||||
return _cursorPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::resetSelection(int pos)
|
|
||||||
{
|
|
||||||
if (pos < 0)
|
|
||||||
pos = 0;
|
|
||||||
pos = pos / 2;
|
|
||||||
_selectionInit = pos;
|
|
||||||
_selectionBegin = pos;
|
|
||||||
_selectionEnd = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::setSelection(int pos)
|
|
||||||
{
|
|
||||||
if (pos < 0)
|
|
||||||
pos = 0;
|
|
||||||
pos = pos / 2;
|
|
||||||
if (pos >= _selectionInit)
|
|
||||||
{
|
|
||||||
_selectionEnd = pos;
|
|
||||||
_selectionBegin = _selectionInit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_selectionBegin = pos;
|
|
||||||
_selectionEnd = _selectionInit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHexEditPrivate::getSelectionBegin()
|
|
||||||
{
|
|
||||||
return _selectionBegin;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHexEditPrivate::getSelectionEnd()
|
|
||||||
{
|
|
||||||
return _selectionEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void QHexEditPrivate::updateCursor()
|
|
||||||
{
|
|
||||||
if (_blink)
|
|
||||||
_blink = false;
|
|
||||||
else
|
|
||||||
_blink = true;
|
|
||||||
update(_cursorX, _cursorY, _charWidth, _charHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHexEditPrivate::adjust()
|
|
||||||
{
|
|
||||||
_charWidth = fontMetrics().width(QLatin1Char('9'));
|
|
||||||
_charHeight = fontMetrics().height();
|
|
||||||
|
|
||||||
_xPosAdr = 0;
|
|
||||||
if (_addressArea)
|
|
||||||
_xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX;
|
|
||||||
else
|
|
||||||
_xPosHex = 0;
|
|
||||||
_xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII;
|
|
||||||
|
|
||||||
// tell QAbstractScollbar, how big we are
|
|
||||||
setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5);
|
|
||||||
setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth));
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
#ifndef QHEXEDIT_P_H
|
|
||||||
#define QHEXEDIT_P_H
|
|
||||||
|
|
||||||
/** \cond docNever */
|
|
||||||
|
|
||||||
|
|
||||||
#include <QtGui>
|
|
||||||
#include "xbytearray.h"
|
|
||||||
|
|
||||||
class QHexEditPrivate : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
QHexEditPrivate(QScrollArea *parent);
|
|
||||||
|
|
||||||
void setAddressAreaColor(QColor const &color);
|
|
||||||
QColor addressAreaColor();
|
|
||||||
|
|
||||||
void setAddressOffset(int offset);
|
|
||||||
int addressOffset();
|
|
||||||
|
|
||||||
void setCursorPos(int position);
|
|
||||||
int cursorPos();
|
|
||||||
|
|
||||||
void setData(QByteArray const &data);
|
|
||||||
QByteArray data();
|
|
||||||
|
|
||||||
void setHighlightingColor(QColor const &color);
|
|
||||||
QColor highlightingColor();
|
|
||||||
|
|
||||||
void setOverwriteMode(bool overwriteMode);
|
|
||||||
bool overwriteMode();
|
|
||||||
|
|
||||||
void setReadOnly(bool readOnly);
|
|
||||||
bool isReadOnly();
|
|
||||||
|
|
||||||
void setSelectionColor(QColor const &color);
|
|
||||||
QColor selectionColor();
|
|
||||||
|
|
||||||
XByteArray & xData();
|
|
||||||
|
|
||||||
void insert(int index, const QByteArray & ba);
|
|
||||||
void insert(int index, char ch);
|
|
||||||
void remove(int index, int len=1);
|
|
||||||
void replace(int index, char ch);
|
|
||||||
void replace(int index, const QByteArray & ba);
|
|
||||||
|
|
||||||
void setAddressArea(bool addressArea);
|
|
||||||
void setAddressWidth(int addressWidth);
|
|
||||||
void setAsciiArea(bool asciiArea);
|
|
||||||
void setHighlighting(bool mode);
|
|
||||||
virtual void setFont(const QFont &font);
|
|
||||||
|
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
|
|
||||||
QString toRedableString();
|
|
||||||
QString selectionToReadableString();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void currentAddressChanged(int address);
|
|
||||||
void currentSizeChanged(int size);
|
|
||||||
void dataChanged();
|
|
||||||
void overwriteModeChanged(bool state);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void keyPressEvent(QKeyEvent * event);
|
|
||||||
void mouseMoveEvent(QMouseEvent * event);
|
|
||||||
void mousePressEvent(QMouseEvent * event);
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *event);
|
|
||||||
|
|
||||||
int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION
|
|
||||||
|
|
||||||
void resetSelection(int pos);
|
|
||||||
void setSelection(int pos); // set min (if below init) or max (if greater init)
|
|
||||||
int getSelectionBegin();
|
|
||||||
int getSelectionEnd();
|
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void updateCursor();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void adjust();
|
|
||||||
|
|
||||||
QColor _addressAreaColor;
|
|
||||||
QColor _highlightingColor;
|
|
||||||
QColor _selectionColor;
|
|
||||||
QScrollArea *_scrollArea;
|
|
||||||
QTimer _cursorTimer;
|
|
||||||
QUndoStack *_undoStack;
|
|
||||||
|
|
||||||
XByteArray _xData; // Hält den Inhalt des Hex Editors
|
|
||||||
|
|
||||||
bool _blink; // true: then cursor blinks
|
|
||||||
bool _renderingRequired; // Flag to store that rendering is necessary
|
|
||||||
bool _addressArea; // left area of QHexEdit
|
|
||||||
bool _asciiArea; // medium area
|
|
||||||
bool _highlighting; // highlighting of changed bytes
|
|
||||||
bool _overwriteMode;
|
|
||||||
bool _readOnly; // true: the user can only look and navigate
|
|
||||||
|
|
||||||
int _charWidth, _charHeight; // char dimensions (dpendend on font)
|
|
||||||
int _cursorX, _cursorY; // graphics position of the cursor
|
|
||||||
int _cursorPosition; // charakter positioin in stream (on byte ends in to steps)
|
|
||||||
int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas
|
|
||||||
|
|
||||||
int _selectionBegin; // First selected char
|
|
||||||
int _selectionEnd; // Last selected char
|
|
||||||
int _selectionInit; // That's, where we pressed the mouse button
|
|
||||||
|
|
||||||
int _size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** \endcond docNever */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
|||||||
#include "xbytearray.h"
|
|
||||||
|
|
||||||
XByteArray::XByteArray()
|
|
||||||
{
|
|
||||||
_oldSize = -99;
|
|
||||||
_addressNumbers = 4;
|
|
||||||
_addressOffset = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int XByteArray::addressOffset()
|
|
||||||
{
|
|
||||||
return _addressOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XByteArray::setAddressOffset(int offset)
|
|
||||||
{
|
|
||||||
_addressOffset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XByteArray::addressWidth()
|
|
||||||
{
|
|
||||||
return _addressNumbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XByteArray::setAddressWidth(int width)
|
|
||||||
{
|
|
||||||
if ((width >= 0) and (width<=6))
|
|
||||||
{
|
|
||||||
_addressNumbers = width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::data()
|
|
||||||
{
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XByteArray::setData(QByteArray data)
|
|
||||||
{
|
|
||||||
_data = data;
|
|
||||||
_changedData = QByteArray(data.length(), char(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XByteArray::dataChanged(int i)
|
|
||||||
{
|
|
||||||
return bool(_changedData[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray XByteArray::dataChanged(int i, int len)
|
|
||||||
{
|
|
||||||
return _changedData.mid(i, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XByteArray::setDataChanged(int i, bool state)
|
|
||||||
{
|
|
||||||
_changedData[i] = char(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XByteArray::setDataChanged(int i, const QByteArray & state)
|
|
||||||
{
|
|
||||||
int length = state.length();
|
|
||||||
int len;
|
|
||||||
if ((i + length) > _changedData.length())
|
|
||||||
len = _changedData.length() - i;
|
|
||||||
else
|
|
||||||
len = length;
|
|
||||||
_changedData.replace(i, len, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
int XByteArray::realAddressNumbers()
|
|
||||||
{
|
|
||||||
if (_oldSize != _data.size())
|
|
||||||
{
|
|
||||||
// is addressNumbers wide enought?
|
|
||||||
QString test = QString("%1")
|
|
||||||
.arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0'));
|
|
||||||
_realAddressNumbers = test.size();
|
|
||||||
}
|
|
||||||
return _realAddressNumbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XByteArray::size()
|
|
||||||
{
|
|
||||||
return _data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::insert(int i, char ch)
|
|
||||||
{
|
|
||||||
_data.insert(i, ch);
|
|
||||||
_changedData.insert(i, char(1));
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::insert(int i, const QByteArray & ba)
|
|
||||||
{
|
|
||||||
_data.insert(i, ba);
|
|
||||||
_changedData.insert(i, QByteArray(ba.length(), char(1)));
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::remove(int i, int len)
|
|
||||||
{
|
|
||||||
_data.remove(i, len);
|
|
||||||
_changedData.remove(i, len);
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::replace(int index, char ch)
|
|
||||||
{
|
|
||||||
_data[index] = ch;
|
|
||||||
_changedData[index] = char(1);
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::replace(int index, const QByteArray & ba)
|
|
||||||
{
|
|
||||||
int len = ba.length();
|
|
||||||
return replace(index, len, ba);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
if ((index + length) > _data.length())
|
|
||||||
len = _data.length() - index;
|
|
||||||
else
|
|
||||||
len = length;
|
|
||||||
_data.replace(index, len, ba.mid(0, len));
|
|
||||||
_changedData.replace(index, len, QByteArray(len, char(1)));
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
QChar XByteArray::asciiChar(int index)
|
|
||||||
{
|
|
||||||
char ch = _data[index];
|
|
||||||
if ((ch < 0x20) or (ch > 0x7e))
|
|
||||||
ch = '.';
|
|
||||||
return QChar(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString XByteArray::toRedableString(int start, int end)
|
|
||||||
{
|
|
||||||
int adrWidth = realAddressNumbers();
|
|
||||||
if (_addressNumbers > adrWidth)
|
|
||||||
adrWidth = _addressNumbers;
|
|
||||||
if (end < 0)
|
|
||||||
end = _data.size();
|
|
||||||
|
|
||||||
QString result;
|
|
||||||
for (int i=start; i < end; i += 16)
|
|
||||||
{
|
|
||||||
QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0'));
|
|
||||||
QString hexStr;
|
|
||||||
QString ascStr;
|
|
||||||
for (int j=0; j<16; j++)
|
|
||||||
{
|
|
||||||
if ((i + j) < _data.size())
|
|
||||||
{
|
|
||||||
hexStr.append(" ").append(_data.mid(i+j, 1).toHex());
|
|
||||||
ascStr.append(asciiChar(i+j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n";
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
#ifndef XBYTEARRAY_H
|
|
||||||
#define XBYTEARRAY_H
|
|
||||||
|
|
||||||
/** \cond docNever */
|
|
||||||
|
|
||||||
#include <QtCore>
|
|
||||||
|
|
||||||
/*! XByteArray represents the content of QHexEcit.
|
|
||||||
XByteArray comprehend the data itself and informations to store if it was
|
|
||||||
changed. The QHexEdit component uses these informations to perform nice
|
|
||||||
rendering of the data
|
|
||||||
|
|
||||||
XByteArray also provides some functionality to insert, replace and remove
|
|
||||||
single chars and QByteArras. Additionally some functions support rendering
|
|
||||||
and converting to readable strings.
|
|
||||||
*/
|
|
||||||
class XByteArray
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit XByteArray();
|
|
||||||
|
|
||||||
int addressOffset();
|
|
||||||
void setAddressOffset(int offset);
|
|
||||||
|
|
||||||
int addressWidth();
|
|
||||||
void setAddressWidth(int width);
|
|
||||||
|
|
||||||
QByteArray & data();
|
|
||||||
void setData(QByteArray data);
|
|
||||||
|
|
||||||
bool dataChanged(int i);
|
|
||||||
QByteArray dataChanged(int i, int len);
|
|
||||||
void setDataChanged(int i, bool state);
|
|
||||||
void setDataChanged(int i, const QByteArray & state);
|
|
||||||
|
|
||||||
int realAddressNumbers();
|
|
||||||
int size();
|
|
||||||
|
|
||||||
QByteArray & insert(int i, char ch);
|
|
||||||
QByteArray & insert(int i, const QByteArray & ba);
|
|
||||||
|
|
||||||
QByteArray & remove(int pos, int len);
|
|
||||||
|
|
||||||
QByteArray & replace(int index, char ch);
|
|
||||||
QByteArray & replace(int index, const QByteArray & ba);
|
|
||||||
QByteArray & replace(int index, int length, const QByteArray & ba);
|
|
||||||
|
|
||||||
QChar asciiChar(int index);
|
|
||||||
QString toRedableString(int start=0, int end=-1);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
private:
|
|
||||||
QByteArray _data;
|
|
||||||
QByteArray _changedData;
|
|
||||||
|
|
||||||
int _addressNumbers; // wanted width of address area
|
|
||||||
int _addressOffset; // will be added to the real addres inside bytearray
|
|
||||||
int _realAddressNumbers; // real width of address area (can be greater then wanted width)
|
|
||||||
int _oldSize; // size of data
|
|
||||||
};
|
|
||||||
|
|
||||||
/** \endcond docNever */
|
|
||||||
#endif // XBYTEARRAY_H
|
|
@ -410,11 +410,16 @@ void AbstractPort::updatePacketListInterleaved()
|
|||||||
|
|
||||||
qDebug("In %s", __FUNCTION__);
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
clearPacketList();
|
||||||
|
if (streamList_.size() == 0)
|
||||||
|
{
|
||||||
|
isSendQueueDirty_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// First sort the streams by ordinalValue
|
// First sort the streams by ordinalValue
|
||||||
qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan);
|
qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan);
|
||||||
|
|
||||||
clearPacketList();
|
|
||||||
|
|
||||||
for (int i = 0; i < streamList_.size(); i++)
|
for (int i = 0; i < streamList_.size(); i++)
|
||||||
{
|
{
|
||||||
if (!streamList_[i]->isEnabled())
|
if (!streamList_[i]->isEnabled())
|
||||||
|
@ -111,6 +111,10 @@ void Device::setIp4(quint32 address, int prefixLength, quint32 gateway)
|
|||||||
ip4PrefixLength_ = prefixLength;
|
ip4PrefixLength_ = prefixLength;
|
||||||
ip4Gateway_ = gateway;
|
ip4Gateway_ = gateway;
|
||||||
hasIp4_ = true;
|
hasIp4_ = true;
|
||||||
|
|
||||||
|
// Precalculate our mask 'n subnet to avoid doing so at pkt rx/tx time
|
||||||
|
ip4Mask_ = ~0 << (32 - ip4PrefixLength_);
|
||||||
|
ip4Subnet_ = ip4_ & ip4Mask_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::setIp6(UInt128 address, int prefixLength, UInt128 gateway)
|
void Device::setIp6(UInt128 address, int prefixLength, UInt128 gateway)
|
||||||
@ -119,6 +123,10 @@ void Device::setIp6(UInt128 address, int prefixLength, UInt128 gateway)
|
|||||||
ip6PrefixLength_ = prefixLength;
|
ip6PrefixLength_ = prefixLength;
|
||||||
ip6Gateway_ = gateway;
|
ip6Gateway_ = gateway;
|
||||||
hasIp6_ = true;
|
hasIp6_ = true;
|
||||||
|
|
||||||
|
// Precalculate our mask 'n subnet to avoid doing so at pkt rx/tx time
|
||||||
|
ip6Mask_ = ~UInt128(0, 0) << (128 - ip6PrefixLength_);
|
||||||
|
ip6Subnet_ = ip6_ & ip6Mask_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::getConfig(OstEmul::Device *deviceConfig)
|
void Device::getConfig(OstEmul::Device *deviceConfig)
|
||||||
@ -405,7 +413,7 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|||||||
// We know only about IP packets
|
// We know only about IP packets
|
||||||
if ((ethType == 0x0800) && hasIp4_) { // IPv4
|
if ((ethType == 0x0800) && hasIp4_) { // IPv4
|
||||||
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
||||||
quint32 dstIp, tgtIp, mask;
|
quint32 dstIp, tgtIp;
|
||||||
|
|
||||||
if (pktBuf->length() < ipHdrLen) {
|
if (pktBuf->length() < ipHdrLen) {
|
||||||
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
||||||
@ -418,14 +426,12 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|||||||
qDebug("mcast dst %x", dstIp);
|
qDebug("mcast dst %x", dstIp);
|
||||||
return (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF);
|
return (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF);
|
||||||
}
|
}
|
||||||
mask = ~0 << (32 - ip4PrefixLength_);
|
tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_;
|
||||||
qDebug("dst %x mask %x self %x", dstIp, mask, ip4_);
|
|
||||||
tgtIp = ((dstIp & mask) == (ip4_ & mask)) ? dstIp : ip4Gateway_;
|
|
||||||
|
|
||||||
return arpTable_.value(tgtIp);
|
return arpTable_.value(tgtIp);
|
||||||
}
|
}
|
||||||
else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6
|
else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6
|
||||||
UInt128 dstIp, tgtIp, mask;
|
UInt128 dstIp, tgtIp;
|
||||||
|
|
||||||
if (pktBuf->length() < (kIp6HdrLen+2)) {
|
if (pktBuf->length() < (kIp6HdrLen+2)) {
|
||||||
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
||||||
@ -439,12 +445,7 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()));
|
qPrintable(QHostAddress(dstIp.toArray()).toString()));
|
||||||
return (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
|
return (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
mask = ~UInt128(0, 0) << (128 - ip6PrefixLength_);
|
tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_;
|
||||||
qDebug("dst %s mask %s self %s",
|
|
||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
|
||||||
qPrintable(QHostAddress(mask.toArray()).toString()),
|
|
||||||
qPrintable(QHostAddress(ip6_.toArray()).toString()));
|
|
||||||
tgtIp = ((dstIp & mask) == (ip6_ & mask)) ? dstIp : ip6Gateway_;
|
|
||||||
|
|
||||||
return ndpTable_.value(tgtIp);
|
return ndpTable_.value(tgtIp);
|
||||||
}
|
}
|
||||||
@ -568,7 +569,7 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
|
|||||||
{
|
{
|
||||||
uchar *pktData = pktBuf->data();
|
uchar *pktData = pktBuf->data();
|
||||||
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
int ipHdrLen = (pktData[0] & 0x0F) << 2;
|
||||||
quint32 srcIp = ip4_, dstIp, mask, tgtIp;
|
quint32 dstIp, tgtIp;
|
||||||
|
|
||||||
if (pktBuf->length() < ipHdrLen) {
|
if (pktBuf->length() < ipHdrLen) {
|
||||||
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
||||||
@ -578,9 +579,7 @@ void Device::sendArpRequest(PacketBuffer *pktBuf)
|
|||||||
|
|
||||||
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
|
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
|
||||||
|
|
||||||
mask = ~0 << (32 - ip4PrefixLength_);
|
tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_;
|
||||||
qDebug("dst %x src %x mask %x", dstIp, srcIp, mask);
|
|
||||||
tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip4Gateway_;
|
|
||||||
|
|
||||||
sendArpRequest(tgtIp);
|
sendArpRequest(tgtIp);
|
||||||
|
|
||||||
@ -684,16 +683,18 @@ void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
|||||||
uchar *pktData = pktBuf->push(20);
|
uchar *pktData = pktBuf->push(20);
|
||||||
uchar origTtl = pktData[8];
|
uchar origTtl = pktData[8];
|
||||||
uchar ipProto = pktData[9];
|
uchar ipProto = pktData[9];
|
||||||
quint32 srcIp, dstIp;
|
quint32 srcIp, dstIp, tgtIp, mask;
|
||||||
quint32 sum;
|
quint32 sum;
|
||||||
|
|
||||||
// Swap src/dst IP addresses
|
// Swap src/dst IP addresses
|
||||||
dstIp = qFromBigEndian<quint32>(pktData + 12); // srcIp in original pkt
|
dstIp = qFromBigEndian<quint32>(pktData + 12); // srcIp in original pkt
|
||||||
srcIp = qFromBigEndian<quint32>(pktData + 16); // dstIp in original pkt
|
srcIp = qFromBigEndian<quint32>(pktData + 16); // dstIp in original pkt
|
||||||
|
|
||||||
if (!arpTable_.contains(dstIp)) {
|
tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_;
|
||||||
|
|
||||||
|
if (!arpTable_.contains(tgtIp)) {
|
||||||
qWarning("%s: mac not found for %s; unable to send IPv4 packet",
|
qWarning("%s: mac not found for %s; unable to send IPv4 packet",
|
||||||
__FUNCTION__, qPrintable(QHostAddress(dstIp).toString()));
|
__FUNCTION__, qPrintable(QHostAddress(tgtIp).toString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +713,7 @@ void Device::sendIp4Reply(PacketBuffer *pktBuf)
|
|||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
*(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum));
|
*(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum));
|
||||||
|
|
||||||
encap(pktBuf, arpTable_.value(dstIp), 0x0800);
|
encap(pktBuf, arpTable_.value(tgtIp), 0x0800);
|
||||||
transmitPacket(pktBuf);
|
transmitPacket(pktBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,7 +797,7 @@ bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol)
|
|||||||
{
|
{
|
||||||
int payloadLen = pktBuf->length();
|
int payloadLen = pktBuf->length();
|
||||||
uchar *p = pktBuf->push(kIp6HdrLen);
|
uchar *p = pktBuf->push(kIp6HdrLen);
|
||||||
quint64 dstMac = ndpTable_.value(dstIp);
|
quint64 dstMac;
|
||||||
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__,
|
qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__,
|
||||||
@ -807,6 +808,10 @@ bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol)
|
|||||||
// In case of mcast, derive dstMac
|
// In case of mcast, derive dstMac
|
||||||
if ((dstIp.hi64() >> 56) == 0xff)
|
if ((dstIp.hi64() >> 56) == 0xff)
|
||||||
dstMac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xffffffff);
|
dstMac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xffffffff);
|
||||||
|
else {
|
||||||
|
UInt128 tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_)? dstIp : ip6Gateway_;
|
||||||
|
dstMac = ndpTable_.value(tgtIp);
|
||||||
|
}
|
||||||
|
|
||||||
if (!dstMac) {
|
if (!dstMac) {
|
||||||
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
||||||
@ -841,16 +846,17 @@ _error_exit:
|
|||||||
void Device::sendIp6Reply(PacketBuffer *pktBuf)
|
void Device::sendIp6Reply(PacketBuffer *pktBuf)
|
||||||
{
|
{
|
||||||
uchar *pktData = pktBuf->push(kIp6HdrLen);
|
uchar *pktData = pktBuf->push(kIp6HdrLen);
|
||||||
UInt128 srcIp, dstIp;
|
UInt128 srcIp, dstIp, tgtIp;
|
||||||
|
|
||||||
// Swap src/dst IP addresses
|
// Swap src/dst IP addresses
|
||||||
dstIp = qFromBigEndian<UInt128>(pktData + 8); // srcIp in original pkt
|
dstIp = qFromBigEndian<UInt128>(pktData + 8); // srcIp in original pkt
|
||||||
srcIp = qFromBigEndian<UInt128>(pktData + 24); // dstIp in original pkt
|
srcIp = qFromBigEndian<UInt128>(pktData + 24); // dstIp in original pkt
|
||||||
|
|
||||||
if (!ndpTable_.contains(dstIp)) {
|
tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_;
|
||||||
|
if (!ndpTable_.contains(tgtIp)) {
|
||||||
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
qWarning("%s: mac not found for %s; unable to send IPv6 packet",
|
||||||
__FUNCTION__,
|
__FUNCTION__,
|
||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()));
|
qPrintable(QHostAddress(tgtIp.toArray()).toString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,7 +866,7 @@ void Device::sendIp6Reply(PacketBuffer *pktBuf)
|
|||||||
// Reset TTL
|
// Reset TTL
|
||||||
pktData[7] = 64;
|
pktData[7] = 64;
|
||||||
|
|
||||||
encap(pktBuf, ndpTable_.value(dstIp), 0x86dd);
|
encap(pktBuf, ndpTable_.value(tgtIp), 0x86dd);
|
||||||
transmitPacket(pktBuf);
|
transmitPacket(pktBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +956,7 @@ _invalid_exit:
|
|||||||
void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
|
void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
|
||||||
{
|
{
|
||||||
uchar *pktData = pktBuf->data();
|
uchar *pktData = pktBuf->data();
|
||||||
UInt128 srcIp = ip6_, dstIp, mask, tgtIp;
|
UInt128 dstIp, tgtIp;
|
||||||
|
|
||||||
if (pktBuf->length() < kIp6HdrLen) {
|
if (pktBuf->length() < kIp6HdrLen) {
|
||||||
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
||||||
@ -960,12 +966,7 @@ void Device::sendNeighborSolicit(PacketBuffer *pktBuf)
|
|||||||
|
|
||||||
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
||||||
|
|
||||||
mask = ~UInt128(0, 0) << (128 - ip6PrefixLength_);
|
tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_;
|
||||||
qDebug("%s: dst %s src %s mask %s", __FUNCTION__,
|
|
||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
|
||||||
qPrintable(QHostAddress(srcIp.toArray()).toString()),
|
|
||||||
qPrintable(QHostAddress(mask.toArray()).toString()));
|
|
||||||
tgtIp = ((dstIp & mask) == (srcIp & mask)) ? dstIp : ip6Gateway_;
|
|
||||||
|
|
||||||
sendNeighborSolicit(tgtIp);
|
sendNeighborSolicit(tgtIp);
|
||||||
}
|
}
|
||||||
|
@ -107,11 +107,15 @@ private: // data
|
|||||||
quint32 ip4_;
|
quint32 ip4_;
|
||||||
int ip4PrefixLength_;
|
int ip4PrefixLength_;
|
||||||
quint32 ip4Gateway_;
|
quint32 ip4Gateway_;
|
||||||
|
quint32 ip4Mask_;
|
||||||
|
quint32 ip4Subnet_;
|
||||||
|
|
||||||
bool hasIp6_;
|
bool hasIp6_;
|
||||||
UInt128 ip6_;
|
UInt128 ip6_;
|
||||||
int ip6PrefixLength_;
|
int ip6PrefixLength_;
|
||||||
UInt128 ip6Gateway_;
|
UInt128 ip6Gateway_;
|
||||||
|
UInt128 ip6Mask_;
|
||||||
|
UInt128 ip6Subnet_;
|
||||||
|
|
||||||
DeviceKey key_;
|
DeviceKey key_;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (!drone->init())
|
if (!drone->init())
|
||||||
{
|
{
|
||||||
exitCode = -1;
|
exitCode = 1;
|
||||||
goto _exit;
|
goto _exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,7 +662,7 @@ void MyService::checkVersion(::google::protobuf::RpcController* controller,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
response->set_result(OstProto::VersionCompatibility::kIncompatible);
|
response->set_result(OstProto::VersionCompatibility::kIncompatible);
|
||||||
response->set_notes(QString("Drone needs client version %1.%2.x")
|
response->set_notes(QString("Drone needs controller version %1.%2.x")
|
||||||
.arg(my[0], my[1]).toStdString());
|
.arg(my[0], my[1]).toStdString());
|
||||||
static_cast<PbRpcController*>(controller)->TriggerDisconnect();
|
static_cast<PbRpcController*>(controller)->TriggerDisconnect();
|
||||||
}
|
}
|
||||||
|
137
test/emultest.py
137
test/emultest.py
@ -21,6 +21,7 @@ from rpc import RpcError
|
|||||||
from protocols.mac_pb2 import mac, Mac
|
from protocols.mac_pb2 import mac, Mac
|
||||||
from protocols.ip4_pb2 import ip4, Ip4
|
from protocols.ip4_pb2 import ip4, Ip4
|
||||||
from protocols.ip6_pb2 import ip6, Ip6
|
from protocols.ip6_pb2 import ip6, Ip6
|
||||||
|
from protocols.icmp_pb2 import icmp, Icmp
|
||||||
from protocols.vlan_pb2 import vlan
|
from protocols.vlan_pb2 import vlan
|
||||||
|
|
||||||
use_defaults = True
|
use_defaults = True
|
||||||
@ -348,6 +349,106 @@ def dut_vlans(request, dut_ports):
|
|||||||
|
|
||||||
request.addfinalizer(delete_vdev)
|
request.addfinalizer(delete_vdev)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ping(request, drone, ip_ver, port_id, src_ip, dst_ip):
|
||||||
|
# create ICMP stream
|
||||||
|
stream_id = ost_pb.StreamIdList()
|
||||||
|
stream_id.port_id.CopyFrom(port_id)
|
||||||
|
stream_id.stream_id.add().id = 0
|
||||||
|
log.info('adding ping tx_stream %d' % stream_id.stream_id[0].id)
|
||||||
|
|
||||||
|
drone.addStream(stream_id)
|
||||||
|
|
||||||
|
# configure the ICMP echo tx stream(s)
|
||||||
|
stream_cfg = ost_pb.StreamConfigList()
|
||||||
|
stream_cfg.port_id.CopyFrom(port_id)
|
||||||
|
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.packets_per_sec = 1
|
||||||
|
s.control.num_packets = 3
|
||||||
|
|
||||||
|
# setup stream protocols as mac:eth2:ip:icmp:payload
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
||||||
|
p.Extensions[mac].dst_mac_mode = Mac.e_mm_resolve
|
||||||
|
p.Extensions[mac].src_mac_mode = Mac.e_mm_resolve
|
||||||
|
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
|
||||||
|
|
||||||
|
if ip_ver == 4:
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
|
||||||
|
ip = None
|
||||||
|
ip = p.Extensions[ip4]
|
||||||
|
ip.src_ip = src_ip
|
||||||
|
ip.dst_ip = dst_ip
|
||||||
|
elif ip_ver == 6:
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kIp6FieldNumber
|
||||||
|
ip = p.Extensions[ip6]
|
||||||
|
ip.src_addr_hi = src_ip.hi
|
||||||
|
ip.src_addr_lo = src_ip.lo
|
||||||
|
ip.dst_addr_hi = dst_ip.hi
|
||||||
|
ip.dst_addr_lo = dst_ip.lo
|
||||||
|
else:
|
||||||
|
assert False # unreachable
|
||||||
|
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kIcmpFieldNumber
|
||||||
|
if ip_ver == 6:
|
||||||
|
p.Extensions[icmp].icmp_version = Icmp.kIcmp6
|
||||||
|
p.Extensions[icmp].type = 128 # icmpv6 echo request
|
||||||
|
|
||||||
|
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
|
||||||
|
|
||||||
|
log.info('configuring ping tx_stream %d' % stream_id.stream_id[0].id)
|
||||||
|
|
||||||
|
drone.modifyStream(stream_cfg)
|
||||||
|
|
||||||
|
# send ping packets
|
||||||
|
ports = ost_pb.PortIdList()
|
||||||
|
ports.port_id.add().id = port_id.id
|
||||||
|
drone.startCapture(ports)
|
||||||
|
drone.startTransmit(ports)
|
||||||
|
time.sleep(5)
|
||||||
|
drone.stopCapture(ports)
|
||||||
|
|
||||||
|
# delete ping stream
|
||||||
|
drone.deleteStream(stream_id)
|
||||||
|
|
||||||
|
# FIXME: workaround for bug#179
|
||||||
|
stream_cfg.ClearField("stream")
|
||||||
|
drone.modifyStream(stream_cfg)
|
||||||
|
|
||||||
|
# verify ICMP Replies are received
|
||||||
|
buff = drone.getCaptureBuffer(port_id)
|
||||||
|
drone.saveCaptureBuffer(buff, 'capture.pcap')
|
||||||
|
log.info('dumping Rx capture buffer (all)')
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap'])
|
||||||
|
print(cap_pkts)
|
||||||
|
if ip_ver == 4:
|
||||||
|
filter = '(icmp.type == 0)' \
|
||||||
|
' && (icmp.code == 0)' \
|
||||||
|
' && (ip.src == ' + str(ipaddress.ip_address(dst_ip)) + ')' \
|
||||||
|
' && (ip.dst == ' + str(ipaddress.ip_address(src_ip)) + ')' \
|
||||||
|
' && !expert.severity'
|
||||||
|
elif ip_ver == 6:
|
||||||
|
filter = '(icmpv6.type == 129)' \
|
||||||
|
' && (icmpv6.code == 0)' \
|
||||||
|
' && (ipv6.src == ' \
|
||||||
|
+ str(ip6_address(dst_ip)) + ')' \
|
||||||
|
' && (ipv6.dst == ' \
|
||||||
|
+ str(ip6_address(src_ip)) + ')' \
|
||||||
|
' && !expert.severity'
|
||||||
|
log.info('dumping Rx capture buffer (filtered)')
|
||||||
|
print filter
|
||||||
|
cap_pkts = subprocess.check_output([tshark, '-nr', 'capture.pcap',
|
||||||
|
'-Y', filter])
|
||||||
|
print(cap_pkts)
|
||||||
|
return cap_pkts.count('\n') > 1
|
||||||
|
|
||||||
# ================================================================= #
|
# ================================================================= #
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
@ -360,7 +461,7 @@ def dut_vlans(request, dut_ports):
|
|||||||
{'ip_ver': [6], 'mac_step': 1, 'ip_step': 1},
|
{'ip_ver': [6], 'mac_step': 1, 'ip_step': 1},
|
||||||
{'ip_ver': [4, 6], 'mac_step': 2, 'ip_step': 5},
|
{'ip_ver': [4, 6], 'mac_step': 2, 'ip_step': 5},
|
||||||
])
|
])
|
||||||
def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
def test_multiEmulDevNoVlan(request, drone, ports, dut, dut_ports, dut_ip,
|
||||||
stream_clear, emul_ports, dgid_list, dev_cfg):
|
stream_clear, emul_ports, dgid_list, dev_cfg):
|
||||||
# ----------------------------------------------------------------- #
|
# ----------------------------------------------------------------- #
|
||||||
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
# TESTCASE: Emulate multiple IPv4 devices (no vlans)
|
||||||
@ -381,9 +482,9 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
|||||||
ip_step = dev_cfg['ip_step']
|
ip_step = dev_cfg['ip_step']
|
||||||
|
|
||||||
# configure the tx device(s)
|
# configure the tx device(s)
|
||||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
tx_devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||||
devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
tx_devgrp_cfg.port_id.CopyFrom(ports.tx.port_id[0])
|
||||||
dg = devgrp_cfg.device_group.add()
|
dg = tx_devgrp_cfg.device_group.add()
|
||||||
dg.device_group_id.id = dgid_list.tx.device_group_id[0].id
|
dg.device_group_id.id = dgid_list.tx.device_group_id[0].id
|
||||||
dg.core.name = "Host1"
|
dg.core.name = "Host1"
|
||||||
dg.device_count = num_devs
|
dg.device_count = num_devs
|
||||||
@ -406,12 +507,12 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
|||||||
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
||||||
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
||||||
|
|
||||||
drone.modifyDeviceGroup(devgrp_cfg)
|
drone.modifyDeviceGroup(tx_devgrp_cfg)
|
||||||
|
|
||||||
# configure the rx device(s)
|
# configure the rx device(s)
|
||||||
devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
rx_devgrp_cfg = ost_pb.DeviceGroupConfigList()
|
||||||
devgrp_cfg.port_id.CopyFrom(ports.rx.port_id[0])
|
rx_devgrp_cfg.port_id.CopyFrom(ports.rx.port_id[0])
|
||||||
dg = devgrp_cfg.device_group.add()
|
dg = rx_devgrp_cfg.device_group.add()
|
||||||
dg.device_group_id.id = dgid_list.rx.device_group_id[0].id
|
dg.device_group_id.id = dgid_list.rx.device_group_id[0].id
|
||||||
dg.core.name = "Host1"
|
dg.core.name = "Host1"
|
||||||
dg.device_count = num_devs
|
dg.device_count = num_devs
|
||||||
@ -434,7 +535,25 @@ def test_multiEmulDevNoVlan(drone, ports, dut, dut_ports, dut_ip,
|
|||||||
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
ip.step.CopyFrom(ip6_address(ip_step).ip6)
|
||||||
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
ip.default_gateway.CopyFrom(ip6addr.gateway)
|
||||||
|
|
||||||
drone.modifyDeviceGroup(devgrp_cfg)
|
drone.modifyDeviceGroup(rx_devgrp_cfg)
|
||||||
|
|
||||||
|
# test end-to-end reachability - after resolving ARP/NDP
|
||||||
|
# FIXME: if and when ping RPC is added, move to where we ping DUT below
|
||||||
|
# FIXME: also add ping test to vlan case
|
||||||
|
time.sleep(10) # wait for DAD? otherwise we don't get replies for NS
|
||||||
|
drone.clearDeviceNeighbors(emul_ports)
|
||||||
|
drone.resolveDeviceNeighbors(emul_ports)
|
||||||
|
time.sleep(3) # wait for ARP resolution
|
||||||
|
# FIXME: if ping6/ping4 order is swapped, DUT does not send NS - have
|
||||||
|
# spent several hours trying to figure out why - to no avail :(
|
||||||
|
if has_ip6:
|
||||||
|
assert ping(request, drone, 6, emul_ports.port_id[0],
|
||||||
|
tx_devgrp_cfg.device_group[0].Extensions[emul.ip6].address,
|
||||||
|
rx_devgrp_cfg.device_group[0].Extensions[emul.ip6].address)
|
||||||
|
if has_ip4:
|
||||||
|
assert ping(request, drone, 4, emul_ports.port_id[0],
|
||||||
|
tx_devgrp_cfg.device_group[0].Extensions[emul.ip4].address,
|
||||||
|
rx_devgrp_cfg.device_group[0].Extensions[emul.ip4].address)
|
||||||
|
|
||||||
# add the tx stream(s) - we may need more than one
|
# add the tx stream(s) - we may need more than one
|
||||||
stream_id = ost_pb.StreamIdList()
|
stream_id = ost_pb.StreamIdList()
|
||||||
|
Loading…
Reference in New Issue
Block a user