Device Emulation (contd.): Implemented display of ARP Cache Device Detail; styled "drillable" fields in DeviceModel suitably

This commit is contained in:
Srivats P 2016-03-14 20:11:40 +05:30
parent 24a93a5025
commit 853802b997
8 changed files with 307 additions and 3 deletions

151
client/arpstatusmodel.cpp Normal file
View File

@ -0,0 +1,151 @@
/*
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 "arpstatusmodel.h"
#include "port.h"
#include "emulproto.pb.h"
#include <QHostAddress>
enum {
kIp4Address,
kMacAddress,
kStatus,
kFieldCount
};
static QStringList columns_ = QStringList()
<< "IPv4 Address"
<< "Mac Address"
<< "Status";
ArpStatusModel::ArpStatusModel(QObject *parent)
: QAbstractTableModel(parent)
{
port_ = NULL;
deviceIndex_ = -1;
neighbors_ = NULL;
}
int ArpStatusModel::rowCount(const QModelIndex &parent) const
{
if (!port_ || deviceIndex_ < 0 || parent.isValid())
return 0;
return port_->numArp(deviceIndex_);
}
int ArpStatusModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return columns_.size();
}
QVariant ArpStatusModel::headerData(
int section,
Qt::Orientation orientation,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
switch (orientation) {
case Qt::Horizontal:
return columns_[section];
case Qt::Vertical:
return QString("%1").arg(section + 1);
default:
Q_ASSERT(false); // Unreachable
}
return QVariant();
}
QVariant ArpStatusModel::data(const QModelIndex &index, int role) const
{
QString str;
if (!port_ || deviceIndex_ < 0 || !index.isValid())
return QVariant();
int arpIdx = index.row();
int field = index.column();
Q_ASSERT(arpIdx < port_->numArp(deviceIndex_));
Q_ASSERT(field < kFieldCount);
const OstEmul::ArpEntry &arp = neighbors_->arp(arpIdx);
switch (field) {
case kIp4Address:
switch (role) {
case Qt::DisplayRole:
return QHostAddress(arp.ip4()).toString();
default:
break;
}
return QVariant();
case kMacAddress:
switch (role) {
case Qt::DisplayRole:
return QString("%1").arg(arp.mac(), 6*2, 16, QChar('0'))
.replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:")
.toUpper();
default:
break;
}
return QVariant();
case kStatus:
switch (role) {
case Qt::DisplayRole:
return arp.mac() ?
QString("Resolved") : QString("Failed");
default:
break;
}
return QVariant();
default:
Q_ASSERT(false); // unreachable!
break;
}
qWarning("%s: Unsupported field #%d", __FUNCTION__, field);
return QVariant();
}
void ArpStatusModel::setDeviceIndex(Port *port, int deviceIndex)
{
port_ = port;
deviceIndex_ = deviceIndex;
if (port_)
neighbors_ = port_->deviceNeighbors(deviceIndex);
reset();
}
void ArpStatusModel::updateArpStatus()
{
reset();
}

55
client/arpstatusmodel.h Normal file
View File

@ -0,0 +1,55 @@
/*
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 _ARP_STATUS_MODEL_H
#define _ARP_STATUS_MODEL_H
#include <QAbstractTableModel>
class Port;
namespace OstEmul {
class DeviceNeighborList;
}
class ArpStatusModel: public QAbstractTableModel
{
Q_OBJECT
public:
ArpStatusModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const;
void setDeviceIndex(Port *port, int deviceIndex);
public slots:
void updateArpStatus();
private:
Port *port_;
int deviceIndex_;
const OstEmul::DeviceNeighborList *neighbors_;
};
#endif

View File

@ -19,11 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "devicemodel.h"
#include "arpstatusmodel.h"
#include "port.h"
#include "emulproto.pb.h"
#include "uint128.h"
#include <QBrush>
#include <QColor>
#include <QFont>
#include <QHostAddress>
enum {
@ -52,6 +56,7 @@ DeviceModel::DeviceModel(QObject *parent)
: QAbstractTableModel(parent)
{
port_ = NULL;
arpStatusModel_ = new ArpStatusModel(this);
}
int DeviceModel::rowCount(const QModelIndex &parent) const
@ -195,10 +200,15 @@ QVariant DeviceModel::data(const QModelIndex &index, int role) const
case kArpInfo:
switch (role) {
case Qt::DisplayRole:
return QString("%1/%2")
if (dev->has_ip4_prefix_length())
return QString("%1/%2")
.arg(port_->numArpResolved(devIdx))
.arg(port_->numArp(devIdx));
else
return QString("--");
default:
if (dev->has_ip4_prefix_length())
return drillableStyle(role);
break;
}
return QVariant();
@ -206,10 +216,15 @@ QVariant DeviceModel::data(const QModelIndex &index, int role) const
case kNdpInfo:
switch (role) {
case Qt::DisplayRole:
return QString("%1/%2")
if (dev->has_ip6_prefix_length())
return QString("%1/%2")
.arg(port_->numNdpResolved(devIdx))
.arg(port_->numNdp(devIdx));
else
return QString("--");
default:
if (dev->has_ip6_prefix_length())
return drillableStyle(role);
break;
}
return QVariant();
@ -220,6 +235,8 @@ QVariant DeviceModel::data(const QModelIndex &index, int role) const
}
qWarning("%s: Unsupported field #%d", __FUNCTION__, field);
_exit:
return QVariant();
}
@ -231,7 +248,41 @@ void DeviceModel::setPort(Port *port)
reset();
}
QAbstractItemModel* DeviceModel::detailModel(const QModelIndex &index)
{
if (!index.isValid())
return NULL;
switch(index.column()) {
case kArpInfo:
arpStatusModel_->setDeviceIndex(port_, index.row());
return arpStatusModel_;
case kNdpInfo:
default:
return NULL;
}
}
void DeviceModel::updateDeviceList()
{
reset();
}
// Style roles for drillable fields
QVariant DeviceModel::drillableStyle(int role) const
{
QFont f;
switch (role) {
case Qt::ToolTipRole:
return QString("Click for details ...");
case Qt::ForegroundRole:
return QBrush(QColor(Qt::blue));
case Qt::FontRole:
f.setUnderline(true);
return f;
default:
break;
}
return QVariant();
}

View File

@ -22,7 +22,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QAbstractTableModel>
class ArpStatusModel;
class Port;
class DeviceModel: public QAbstractTableModel
{
Q_OBJECT
@ -37,12 +39,16 @@ public:
QVariant data(const QModelIndex &index, int role) const;
void setPort(Port *port);
QAbstractItemModel* detailModel(const QModelIndex &index);
public slots:
void updateDeviceList();
private:
QVariant drillableStyle(int role) const;
Port *port_;
ArpStatusModel *arpStatusModel_;
};
#endif

View File

@ -32,11 +32,14 @@ DevicesWidget::DevicesWidget(QWidget *parent)
deviceGroupList->setVisible(deviceConfig->isChecked());
deviceList->setVisible(deviceInfo->isChecked());
refresh->setVisible(deviceInfo->isChecked());
deviceDetail->hide();
deviceGroupList->verticalHeader()->setDefaultSectionSize(
deviceGroupList->verticalHeader()->minimumSectionSize());
deviceList->verticalHeader()->setDefaultSectionSize(
deviceList->verticalHeader()->minimumSectionSize());
deviceDetail->verticalHeader()->setDefaultSectionSize(
deviceDetail->verticalHeader()->minimumSectionSize());
// Populate DeviceGroup Context Menu Actions
deviceGroupList->addAction(actionNewDeviceGroup);
@ -68,6 +71,10 @@ void DevicesWidget::setPortGroupList(PortGroupList *portGroups)
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
SLOT(updateDeviceViewActions()));
connect(deviceList->selectionModel(),
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
SLOT(when_deviceList_currentChanged(const QModelIndex&)));
// FIXME: hardcoding
deviceGroupList->resizeColumnToContents(1); // Vlan Count
deviceGroupList->resizeColumnToContents(2); // Device Count
@ -147,6 +154,7 @@ void DevicesWidget::on_deviceInfo_toggled(bool checked)
refresh->setVisible(checked);
deviceGroupList->setHidden(checked);
deviceList->setVisible(checked);
deviceDetail->hide();
}
void DevicesWidget::on_actionNewDeviceGroup_triggered()
@ -215,3 +223,15 @@ void DevicesWidget::on_refresh_clicked()
portGroups_->portGroup(curPortGroup)
.getDeviceInfo(portGroups_->port(currentPortIndex_).id());
}
void DevicesWidget::when_deviceList_currentChanged(const QModelIndex &index)
{
if (!index.isValid() || !portGroups_)
return;
QAbstractItemModel *detailModel = portGroups_->getDeviceModel()
->detailModel(index);
deviceDetail->setModel(detailModel);
deviceDetail->setVisible(detailModel != NULL);
}

View File

@ -48,6 +48,8 @@ private slots:
void on_refresh_clicked();
void when_deviceList_currentChanged(const QModelIndex &index);
private:
PortGroupList *portGroups_;
QModelIndex currentPortIndex_;

View File

@ -86,7 +86,24 @@
</widget>
</item>
<item>
<widget class="QTableView" name="deviceList" />
<widget class="QTableView" name="deviceList" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="selectionBehavior" >
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="deviceDetail" >
<property name="selectionMode" >
<enum>QAbstractItemView::SingleSelection</enum>
</property>
</widget>
</item>
</layout>
<action name="actionNewDeviceGroup" >

View File

@ -33,6 +33,7 @@ LIBS += -lprotobuf
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
RESOURCES += ostinato.qrc
HEADERS += \
arpstatusmodel.h \
devicegroupdialog.h \
devicegroupmodel.h \
devicemodel.h \
@ -73,6 +74,7 @@ FORMS += \
variablefieldswidget.ui
SOURCES += \
arpstatusmodel.cpp \
devicegroupdialog.cpp \
devicegroupmodel.cpp \
devicemodel.cpp \