diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000..a9af8d4 Binary files /dev/null and b/client/icons/bullet_white.png differ diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 48ab66a..1b09036 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -7,6 +7,7 @@ icons/bullet_green.png icons/bullet_orange.png icons/bullet_red.png + icons/bullet_white.png icons/bullet_yellow.png icons/control_play.png icons/control_stop.png diff --git a/client/port.cpp b/client/port.cpp index 291b1b5..d7e2b4c 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -183,6 +183,13 @@ void Port::when_syncComplete() void Port::updateStats(OstProto::PortStats *portStats) { + OstProto::PortState oldState; + + oldState = stats.state(); stats.MergeFrom(*portStats); +#if 0 + if (oldState.link_state() != stats.state().link_state()) + emit portDataChanged(mPortGroupId, mPortId); +#endif } diff --git a/client/port.h b/client/port.h index 126b28d..bdafdb9 100644 --- a/client/port.h +++ b/client/port.h @@ -30,7 +30,6 @@ private: void reorderStreamsByOrdinals(); public: enum AdminStatus { AdminDisable, AdminEnable }; - enum OperStatus { OperDown, OperUp }; enum ControlMode { ControlShared, ControlExclusive }; // FIXME(HIGH): default args is a hack for QList operations on Port @@ -48,8 +47,6 @@ public: { return QString().fromStdString(d.description()); } AdminStatus adminStatus() { return (d.is_enabled()?AdminEnable:AdminDisable); } - OperStatus operStatus() - { return (d.is_oper_up()?OperUp:OperDown); } ControlMode controlMode() { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } @@ -70,6 +67,9 @@ public: Q_ASSERT(index < mStreams.size()); return mStreams[index]; } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + OstProto::PortStats getStats() { return stats; } // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal @@ -97,6 +97,11 @@ public: void updateStats(OstProto::PortStats *portStats); +#if 0 +signals: + void portDataChanged(int portGroupId, int portId); +#endif + }; #endif diff --git a/client/portgroup.h b/client/portgroup.h index c74640a..81363cc 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -91,7 +91,7 @@ public: void processClearStatsAck(OstProto::Ack *ack); signals: - void portGroupDataChanged(PortGroup* portGroup); + void portGroupDataChanged(PortGroup* portGroup, int portId = 0xFFFF); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); void statsChanged(quint32 portGroupId); diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index a503449..4387aee 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -60,8 +60,8 @@ void PortGroupList::addPortGroup(PortGroup &portGroup) { mPortGroupListModel.portGroupAboutToBeAppended(); - connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*)), - &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*))); + connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*, int))); #if 0 connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 61f9be8..9397cc1 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -144,7 +144,17 @@ QVariant PortModel::data(const QModelIndex &index, int role) const DBG0("Exit PortModel data 5\n"); if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) return QVariant(); - return QIcon(":/icons/bullet_green.png"); + switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()].linkState()) + { + case OstProto::LinkStateUnknown: + return QIcon(":/icons/bullet_white.png"); + case OstProto::LinkStateDown: + return QIcon(":/icons/bullet_red.png"); + case OstProto::LinkStateUp: + return QIcon(":/icons/bullet_green.png"); + default: + qFatal("unexpected/unimplemented port oper state"); + } } else { @@ -152,6 +162,8 @@ QVariant PortModel::data(const QModelIndex &index, int role) const return QVariant(); } } + + return QVariant(); } QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -252,7 +264,7 @@ quint32 PortModel::portId(const QModelIndex& index) // ---------------------------------------------- // Slots // ---------------------------------------------- -void PortModel::when_portGroupDataChanged(PortGroup* portGroup) +void PortModel::when_portGroupDataChanged(PortGroup* portGroup, int portId) { QModelIndex index; @@ -266,7 +278,7 @@ void PortModel::when_portGroupDataChanged(PortGroup* portGroup) qDebug("when_portGroupDataChanged idx = %d", pgl->mPortGroups.indexOf(portGroup)); index = createIndex(pgl->mPortGroups.indexOf(portGroup), 0, - (portGroup->id() << 16) | 0xFFFF); + (portGroup->id() << 16) | portId); emit dataChanged(index, index); } diff --git a/client/portmodel.h b/client/portmodel.h index b68acdc..de2b140 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -32,7 +32,7 @@ class PortModel : public QAbstractItemModel private slots: - void when_portGroupDataChanged(PortGroup *portGroup); + void when_portGroupDataChanged(PortGroup *portGroup, int portId); void portGroupAboutToBeAppended(); void portGroupAppended(); diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 9961e12..6d77945 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -67,13 +67,15 @@ void PortStatsModel::getDomainIndexes(const QModelIndex &index, QVariant PortStatsModel::data(const QModelIndex &index, int role) const { uint pgidx, pidx; + int row; // Check for a valid index if (!index.isValid()) return QVariant(); // Check for row/column limits - if (index.row() >= e_STAT_MAX) + row = index.row(); + if (row >= e_STAT_MAX) return QVariant(); if (numPorts.isEmpty()) @@ -91,8 +93,19 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx].getStats(); - switch(index.row()) + switch(row) { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics case e_STAT_FRAMES_RCVD: return stats.rx_pkts(); @@ -136,6 +149,15 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const return 0; } } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } else return QVariant(); diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index de1c585..02f02dd 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -5,7 +5,19 @@ #include typedef enum { - e_STAT_FRAMES_RCVD = 0, + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, e_STAT_FRAMES_SENT, e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_RECV_RATE, @@ -19,10 +31,17 @@ typedef enum { e_STAT_BYTES_RCVD_NIC, e_STAT_BYTES_SENT_NIC, #endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + e_STAT_MAX } PortStat; static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + << "Frames Received" << "Frames Sent" << "Frame Send Rate (fps)" @@ -39,6 +58,17 @@ static QStringList PortStatName = (QStringList() #endif ); +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + class PortGroupList; class PortStatsModel : public QAbstractTableModel diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 8b75b65..7d9bbc4 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -36,6 +36,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) this, SLOT(when_portModel_dataChanged(const QModelIndex&, const QModelIndex&))); + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portView_currentChanged(const QModelIndex&, @@ -139,7 +142,11 @@ void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, { updatePortViewActions(tvPortList->currentIndex()); } - +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } #if 0 diff --git a/client/portswindow.h b/client/portswindow.h index a5bc562..b291f91 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -40,6 +40,7 @@ private slots: void when_streamView_selectionChanged(); void when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); + void when_portModel_reset(); void on_pbApply_clicked(); diff --git a/client/portswindow.ui b/client/portswindow.ui index b58c356..1ed9485 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -6,7 +6,7 @@ 0 0 689 - 389 + 254 @@ -28,10 +28,28 @@ - 1 + 0 + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 6 + @@ -118,11 +136,32 @@ Control + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + Qt::ActionsContextMenu + + QFrame::NoFrame + + + 0 + QAbstractItemView::ExtendedSelection diff --git a/common/protocol.proto b/common/protocol.proto index 1f3c0a5..6557dbb 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -128,7 +128,6 @@ message Port { optional string name = 2; optional string description = 3; optional bool is_enabled = 4; - optional bool is_oper_up = 5; optional bool is_exclusive_control = 6; } @@ -149,9 +148,24 @@ message CaptureBufferList { repeated CaptureBuffer list = 1; } +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + message PortStats { + required PortId port_id = 1; + optional PortState state = 2; + optional uint64 rx_pkts = 11; optional uint64 rx_bytes = 12; optional uint64 rx_pkts_nic = 13; diff --git a/server/myservice.cpp b/server/myservice.cpp index cb9a2db..54b0895 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -68,6 +68,20 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) this->dev = dev; +#ifdef Q_OS_WIN32 + adapter = PacketOpenAdapter(dev->name); + if (!adapter) + qFatal("Unable to open adapter %s", dev->name); + oidData = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + sizeof(uint)); + if (oidData) + { + memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + oidData->Length=sizeof(uint); + } + else + qFatal("failed to alloc oidData"); +#endif + /* * Get 2 device handles - one for rx and one for tx. If we use only * one handle for both rx and tx anythin that we tx using the single @@ -131,12 +145,13 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) if (dev->description) d.set_description(dev->description); d.set_is_enabled(true); //! \todo (LOW) admin enable/disable of port - d.set_is_oper_up(true); //! \todo (HIGH) oper up/down of port d.set_is_exclusive_control(false); //! \todo (HIGH) port exclusive control memset((void*) &stats, 0, sizeof(stats)); resetStats(); + linkState = OstProto::LinkStateUnknown; + // We'll create sendqueue later when required sendQueueList.clear(); returnToQIdx = -1; @@ -149,6 +164,35 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) monitorTx.start(); } +void PortInfo::updateLinkState() +{ +#ifdef Q_OS_WIN32 + OstProto::LinkState newLinkState + = OstProto::LinkStateUnknown; + + memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + oidData->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + oidData->Length = sizeof(uint); + if (PacketRequest(adapter, 0, oidData)) + { + uint state; + + if (oidData->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)oidData->Data, oidData->Length); + if (state == 0) + newLinkState = OstProto::LinkStateUp; + else if (state == 1) + newLinkState = OstProto::LinkStateDown; + } + } + + linkState = newLinkState; +#elif defined(Q_OS_LINUX) + //! \todo (HI) implement link state for linux - get from /proc maybe? +#endif +} + void PortInfo::update() { uchar pktBuf[2000]; @@ -211,6 +255,9 @@ void PortInfo::update() { int len; + /*! \todo (HIGH) if pkt contents do not change across + pkts then don't call makePacket(), rather reuse the + previous */ len = streamList[i]->makePacket(pktBuf, sizeof(pktBuf), j * numPackets + k); if (len > 0) @@ -722,55 +769,60 @@ PortInfo::PortCapture::~PortCapture() void PortInfo::PortCapture::run() { int ret; + char errbuf[PCAP_ERRBUF_SIZE]; + capHandle = pcap_open_live(port->dev->name, 65535, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); if (capHandle == NULL) { - char errbuf[PCAP_ERRBUF_SIZE]; - - capHandle = pcap_open_live(port->dev->name, 65535, - PCAP_OPENFLAG_PROMISCUOUS, -1, errbuf); - if (capHandle == NULL) - { - qDebug("Error opening port %s: %s\n", - port->dev->name, pcap_geterr(capHandle)); - } + qDebug("Error opening port %s: %s\n", + port->dev->name, pcap_geterr(capHandle)); + return; } + if (!capFile.isOpen()) { if (!capFile.open()) qFatal("Unable to open temp cap file"); + return; } qDebug("cap file = %s", capFile.fileName().toAscii().constData()); dumpHandle = pcap_dump_open(capHandle, capFile.fileName().toAscii().constData()); - ret = pcap_loop(capHandle, -1, pcap_dump, (uchar*) dumpHandle); - switch (ret) + m_stop = 0; + while (m_stop == 0) { - case -2: - qDebug("%s: breakloop called %d", __PRETTY_FUNCTION__, ret); - break; + struct pcap_pkthdr *hdr; + const uchar *data; - case -1: - case 0: - qFatal("%s: unexpected break from loop (%d): %s", - __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); - break; - default: - qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + ret = pcap_next_ex(capHandle, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle, hdr, data); + case 0: + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } } + m_stop = 0; + pcap_dump_close(dumpHandle); + pcap_close(capHandle); + dumpHandle = NULL; + capHandle = NULL; } void PortInfo::PortCapture::stop() { - pcap_breakloop(capHandle); - if (dumpHandle) - { - pcap_dump_flush(dumpHandle); - pcap_dump_close(dumpHandle); - dumpHandle = NULL; - } + m_stop = 1; } QFile* PortInfo::PortCapture::captureFile() @@ -1200,6 +1252,7 @@ const ::OstProto::PortIdList* request, { uint portidx; ::OstProto::PortStats *s; + OstProto::PortState *st; portidx = request->port_id(i).id(); if (portidx >= numPorts) @@ -1216,6 +1269,13 @@ const ::OstProto::PortIdList* request, } #endif + portInfo[portidx]->updateLinkState(); + + st = s->mutable_state(); + st->set_link_state(portInfo[portidx]->linkState); + st->set_is_transmit_on(portInfo[portidx]->transmitter.isRunning()); + st->set_is_capture_on(portInfo[portidx]->capturer.isRunning()); + s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - portInfo[portidx]->epochStats.rxPkts); s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - diff --git a/server/myservice.h b/server/myservice.h index fb55283..ebd1db1 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -22,6 +22,10 @@ #ifdef Q_OS_WIN32 #include #endif + +#ifdef Q_OS_WIN32 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#endif #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 @@ -29,6 +33,7 @@ //! 7 byte Preamble + 1 byte SFD + 4 byte FCS #define ETH_FRAME_HDR_SIZE 12 + class MyService; class StreamInfo : public StreamBase @@ -99,6 +104,7 @@ class PortInfo friend class PortInfo; PortInfo *port; + int m_stop; pcap_t *capHandle; pcap_dumper_t *dumpHandle; QTemporaryFile capFile; @@ -110,8 +116,14 @@ class PortInfo void stop(); QFile* captureFile(); }; + +#ifdef Q_OS_WIN32 + LPADAPTER adapter; + PPACKET_OID_DATA oidData; +#endif OstProto::Port d; + OstProto::LinkState linkState; struct PortStats { @@ -167,6 +179,7 @@ class PortInfo public: PortInfo(uint id, pcap_if_t *dev); uint id() { return d.port_id().id(); } + void updateLinkState(); bool isDirty() { return isSendQueueDirty; } void setDirty(bool dirty) { isSendQueueDirty = dirty; } void update();