diff --git a/client/ostinato.pro b/client/ostinato.pro index 572abb2..ec44eb0 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -20,6 +20,7 @@ HEADERS += \ portstatswindow.h \ portswindow.h \ streamconfigdialog.h \ + streamlistdelegate.h \ streammodel.h FORMS += \ @@ -45,6 +46,7 @@ SOURCES += \ portstatswindow.cpp \ portswindow.cpp \ streamconfigdialog.cpp \ + streamlistdelegate.cpp \ streammodel.cpp # Protocol Buffer Sources diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 27dec05..1789e5e 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,18 +1,20 @@ #include "portswindow.h" +#include "streamlistdelegate.h" #include "streamconfigdialog.h" #include #include PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) { + StreamListDelegate *delegate = new StreamListDelegate; //slm = new StreamListModel(); //plm = new PortGroupList(); plm = pgl; setupUi(this); - tvStreamList->horizontalHeader()->resizeSections( - QHeaderView::ResizeToContents); + tvStreamList->setItemDelegate(delegate); + tvStreamList->verticalHeader()->setDefaultSectionSize( tvStreamList->verticalHeader()->minimumSectionSize()); @@ -53,6 +55,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); #endif + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + // Initially we don't have any ports/streams - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); when_streamView_currentChanged(QModelIndex(), QModelIndex()); diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 7ee59f7..e7f1f0b 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -173,7 +173,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, pbPrev->setDisabled(true); pbNext->setDisabled(true); //! \todo Support Goto Stream Id - rbActionGotoStream->setDisabled(true); + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); } @@ -824,10 +825,11 @@ void StreamConfigDialog::LoadCurrentStream() leNumPackets->setText(QString().setNum(pStream->numPackets())); leNumBursts->setText(QString().setNum(pStream->numBursts())); - lePacketsPerBurst->setText(QString().setNum( - pStream->burstSize())); + lePacketsPerBurst->setText(QString().setNum(pStream->burstSize())); lePacketsPerSec->setText(QString().setNum(pStream->packetRate())); leBurstsPerSec->setText(QString().setNum(pStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); } } diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index b94e107..178e966 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -1915,6 +1915,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f67b99e --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a4e8d35 --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,29 @@ +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 32dce31..6680f58 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -1,3 +1,4 @@ +#include "stream.h" #include "streammodel.h" #include "portgrouplist.h" #include "qicon.h" @@ -38,17 +39,19 @@ Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const flags |= Qt::ItemIsEditable; break; case StreamStatus: -#if 0 - flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable; -#endif + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: flags |= Qt::ItemIsEditable; break; default: + //qFatal("Missed case in switch!"); break; } return flags; } + QVariant StreamModel::data(const QModelIndex &index, int role) const { // Check for a valid index @@ -72,10 +75,6 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole) return QIcon(":/icons/stream_edit.png"); -#if 0 - else if ((role == Qt::DisplayRole)) - return QString("EDIT"); -#endif else return QVariant(); break; @@ -90,12 +89,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } case StreamStatus: { - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->streamByIndex(index.row()).isEnabled(); - else - return QVariant(); - #if 0 - if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) + if ((role == Qt::CheckStateRole)) { if (mCurrentPort->streamByIndex(index.row()).isEnabled()) return Qt::Checked; @@ -104,11 +98,23 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } else return QVariant(); - #endif + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row()).nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + break; } default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); + qFatal("-------------UNHANDLED STREAM FIELD----------------"); } return QVariant(); @@ -119,24 +125,35 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r if (mCurrentPort == NULL) return false; - if (index.isValid() && role == Qt::EditRole) + if (index.isValid()) { - // Edit Supported Fields switch (index.column()) { + // Edit Supported Fields case StreamName: mCurrentPort->streamByIndex(index.row()).setName(value.toString()); + emit(dataChanged(index, index)); return true; - break; + case StreamStatus: mCurrentPort->streamByIndex(index.row()).setIsEnabled(value.toBool()); + emit(dataChanged(index, index)); return true; - break; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row()).setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; // Edit Not Supported Fields case StreamIcon: return false; - break; // Unhandled Stream Field default: @@ -144,6 +161,7 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r break; } } + return false; } @@ -157,13 +175,16 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r switch(section) { case StreamIcon: - return QString("Icon"); + return QString(""); break; case StreamName: return QString("Name"); break; case StreamStatus: - return QString("Enabled"); + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); break; default: qDebug("-------------UNHANDLED STREAM FIELD----------------"); diff --git a/client/streammodel.h b/client/streammodel.h index 697517d..95af6fe 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -1,6 +1,7 @@ #ifndef _STREAM_MODEL_H #define _STREAM_MODEL_H +#include #include #include "port.h" @@ -10,14 +11,6 @@ class StreamModel : public QAbstractTableModel { Q_OBJECT - enum StreamFields { - StreamIcon = 0, - StreamName, - StreamStatus, - - StreamMaxFields - }; - Port *mCurrentPort; PortGroupList *pgl; @@ -43,6 +36,19 @@ class StreamModel : public QAbstractTableModel { return &mCurrentPort->mStreams; } #endif + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + public slots: void setCurrentPortIndex(const QModelIndex ¤t); diff --git a/server/myservice.cpp b/server/myservice.cpp index dd19090..085f6f5 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -559,6 +559,7 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) // We'll create sendqueue later when required sendQueueList.clear(); + returnToQIdx = -1; pcapExtra.txPkts = 0; pcapExtra.txBytes = 0; isSendQueueDirty=true; @@ -581,6 +582,8 @@ void PortInfo::update() foreach(sendQ, sendQueueList) pcap_sendqueue_destroy(sendQ.sendQueue); } + sendQueueList.clear(); + returnToQIdx = -1; // TODO(LOW): calculate sendqueue size sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); @@ -589,8 +592,11 @@ void PortInfo::update() // First sort the streams by ordinalValue qSort(streamList); + pktHdr.ts.tv_sec = 0; + pktHdr.ts.tv_usec = 0; for (int i = 0; i < streamList.size(); i++) { +//_restart: if (streamList[i].d.core().is_enabled()) { long numPackets, numBursts; @@ -619,8 +625,6 @@ void PortInfo::update() numBursts, numPackets); qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); - pktHdr.ts.tv_sec = 0; - pktHdr.ts.tv_usec = 0; for (int j = 0; j < numBursts; j++) { for (int k = 0; k < numPackets; k++) @@ -655,6 +659,9 @@ void PortInfo::update() #endif } + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, pktHdr.ts.tv_sec, pktHdr.ts.tv_usec); + if (-1 == pcap_sendqueue_queue(sendQ.sendQueue, &pktHdr, (u_char*) pktBuf)) { @@ -664,17 +671,48 @@ void PortInfo::update() else sendQ.sendQueueCumLen.append(sendQ.sendQueue->len); } - } + } // for (numPackets) pktHdr.ts.tv_usec += ibg; if (pktHdr.ts.tv_usec > 1000000) { pktHdr.ts.tv_sec++; pktHdr.ts.tv_usec -= 1000000; } - } - } - } + } // for (numBursts) + switch(streamList[i].d.control().next()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + // TODO(MED): define and use + // streamList[i].d.control().goto_stream_id(); + + // TODO(MED): assumes goto Id is less than current!!!! + // To support goto to any id, do + // if goto_id > curr_id then + // i = goto_id; + // goto restart; + // else + // returnToQIdx = 0; + + returnToQIdx=0; + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList[i].d.control().next() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: // The last alloc'ed sendQ appended here sendQueueList.append(sendQ); @@ -1061,8 +1099,9 @@ void PortInfo::PortTransmitter::run() m_stop = 0; ost_pcap_sendqueue_list_transmit(port->devHandleRx, port->sendQueueList, - true, &m_stop, &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, - QThread::usleep); + port->returnToQIdx, true, &m_stop, + &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, + QThread::usleep); m_stop = 0; } diff --git a/server/myservice.h b/server/myservice.h index 7f1cb88..bbf019c 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -133,6 +133,7 @@ class PortInfo pcap_t *devHandleRx; pcap_t *devHandleTx; QList sendQueueList; + int returnToQIdx; // FIXME(MED): combine with sendQList bool isSendQueueDirty; PcapExtra pcapExtra; PortMonitorRx monitorRx; diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 78d57e0..3246397 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -62,20 +62,32 @@ int pcap_sendqueue_queue (pcap_send_queue *queue, #endif u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int sync, + QList sendQueueList, int returnToQIdx, int sync, int *p_stop, quint64* p_pkts, quint64* p_bytes, void (*pf_usleep)(ulong)) { - uint ret = 0; + uint i, ret = 0; + ost_pcap_send_queue sq; - foreach(ost_pcap_send_queue sq, sendQueueList) + for(i = 0; i < sendQueueList.size(); i++) { +_restart: + sq = sendQueueList.at(i); ret += ost_pcap_sendqueue_transmit(p, sq.sendQueue, sync, p_stop, p_pkts, p_bytes, pf_usleep); + if (*p_stop) + return ret; + // TODO(HI): Timing between subsequent sendQueues } + if (returnToQIdx >= 0) + { + i = returnToQIdx; + goto _restart; + } + return ret; } diff --git a/server/pcapextra.h b/server/pcapextra.h index 3d2f8f4..5c597df 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -17,7 +17,7 @@ struct ost_pcap_send_queue // Common for all OS - *nix or Win32 u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int sync, + QList sendQueueList, int returnToQIdx, int sync, int *p_stop, quint64* p_pkts, quint64* p_bytes, void (*pf_usleep)(ulong));