diff --git a/client/arpstatusmodel.cpp b/client/arpstatusmodel.cpp
new file mode 100644
index 0000000..cd6af5d
--- /dev/null
+++ b/client/arpstatusmodel.cpp
@@ -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
+*/
+
+#include "arpstatusmodel.h"
+
+#include "port.h"
+
+#include "emulproto.pb.h"
+
+#include
+
+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();
+}
diff --git a/client/arpstatusmodel.h b/client/arpstatusmodel.h
new file mode 100644
index 0000000..64debaa
--- /dev/null
+++ b/client/arpstatusmodel.h
@@ -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
+*/
+
+#ifndef _ARP_STATUS_MODEL_H
+#define _ARP_STATUS_MODEL_H
+
+#include
+
+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
+
diff --git a/client/devicemodel.cpp b/client/devicemodel.cpp
index c99faf7..a261b4a 100644
--- a/client/devicemodel.cpp
+++ b/client/devicemodel.cpp
@@ -19,11 +19,15 @@ along with this program. If not, see
#include "devicemodel.h"
+#include "arpstatusmodel.h"
#include "port.h"
#include "emulproto.pb.h"
#include "uint128.h"
+#include
+#include
+#include
#include
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();
+}
+
diff --git a/client/devicemodel.h b/client/devicemodel.h
index 5e161ff..78637d5 100644
--- a/client/devicemodel.h
+++ b/client/devicemodel.h
@@ -22,7 +22,9 @@ along with this program. If not, see
#include
+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
diff --git a/client/deviceswidget.cpp b/client/deviceswidget.cpp
index 9aa5bf5..789a150 100644
--- a/client/deviceswidget.cpp
+++ b/client/deviceswidget.cpp
@@ -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);
+}
diff --git a/client/deviceswidget.h b/client/deviceswidget.h
index fb15ef6..8c7626e 100644
--- a/client/deviceswidget.h
+++ b/client/deviceswidget.h
@@ -48,6 +48,8 @@ private slots:
void on_refresh_clicked();
+ void when_deviceList_currentChanged(const QModelIndex &index);
+
private:
PortGroupList *portGroups_;
QModelIndex currentPortIndex_;
diff --git a/client/deviceswidget.ui b/client/deviceswidget.ui
index 7753e8f..6ac8adf 100644
--- a/client/deviceswidget.ui
+++ b/client/deviceswidget.ui
@@ -86,7 +86,24 @@
-
-
+
+
+
+ 0
+ 1
+
+
+
+ QAbstractItemView::SelectRows
+
+
+
+ -
+
+
+ QAbstractItemView::SingleSelection
+
+
diff --git a/client/ostinato.pro b/client/ostinato.pro
index 7c20029..75f0604 100644
--- a/client/ostinato.pro
+++ b/client/ostinato.pro
@@ -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 \