/*
Copyright (C) 2010 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 "streambase.h"
#include "abstractprotocol.h"
#include "protocollist.h"
#include "protocollistiterator.h"
#include "protocolmanager.h"
extern ProtocolManager *OstProtocolManager;
extern quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex);
extern quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex);
StreamBase::StreamBase(int portId) :
portId_(portId),
mStreamId(new OstProto::StreamId),
mCore(new OstProto::StreamCore),
mControl(new OstProto::StreamControl)
{
AbstractProtocol *proto;
ProtocolListIterator *iter;
mStreamId->set_id(0xFFFFFFFF);
currentFrameProtocols = new ProtocolList;
iter = createProtocolListIterator();
// By default newly created streams have the mac and payload protocols
proto = OstProtocolManager->createProtocol(
OstProto::Protocol::kMacFieldNumber, this);
iter->insert(proto);
qDebug("stream: mac = %p", proto);
proto = OstProtocolManager->createProtocol(
OstProto::Protocol::kPayloadFieldNumber, this);
iter->insert(proto);
qDebug("stream: payload = %p", proto);
#ifndef QT_NO_DEBUG_OUTPUT
{
iter->toFront();
while (iter->hasNext())
{
qDebug("{{%p}}", iter->next());
// qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber());
}
iter->toFront();
while (iter->hasNext())
{
qDebug("{[%d]}", iter->next()->protocolNumber());
// qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber());
}
}
#endif
delete iter;
}
StreamBase::~StreamBase()
{
currentFrameProtocols->destroy();
delete currentFrameProtocols;
delete mControl;
delete mCore;
delete mStreamId;
}
void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream)
{
AbstractProtocol *proto;
ProtocolListIterator *iter;
mStreamId->CopyFrom(stream.stream_id());
mCore->CopyFrom(stream.core());
mControl->CopyFrom(stream.control());
currentFrameProtocols->destroy();
iter = createProtocolListIterator();
for (int i=0; i < stream.protocol_size(); i++)
{
int protoId = stream.protocol(i).protocol_id().id();
if (!OstProtocolManager->isRegisteredProtocol(protoId))
{
qWarning("Skipping unregistered protocol %d", protoId);
continue;
}
proto = OstProtocolManager->createProtocol(protoId, this);
proto->commonProtoDataCopyFrom(stream.protocol(i));
proto->protoDataCopyFrom(stream.protocol(i));
iter->insert(proto);
}
delete iter;
}
void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const
{
stream.mutable_stream_id()->CopyFrom(*mStreamId);
stream.mutable_core()->CopyFrom(*mCore);
stream.mutable_control()->CopyFrom(*mControl);
stream.clear_protocol();
foreach (const AbstractProtocol* proto, *currentFrameProtocols)
{
OstProto::Protocol *p;
p = stream.add_protocol();
proto->commonProtoDataCopyInto(*p);
proto->protoDataCopyInto(*p);
}
}
#if 0
ProtocolList StreamBase::frameProtocol()
{
return currentFrameProtocols;
}
void StreamBase::setFrameProtocol(ProtocolList protocolList)
{
//currentFrameProtocols.destroy();
currentFrameProtocols = protocolList;
}
#endif
ProtocolListIterator* StreamBase::createProtocolListIterator() const
{
return new ProtocolListIterator(*currentFrameProtocols);
}
quint32 StreamBase::id()
{
return mStreamId->id();
}
bool StreamBase::setId(quint32 id)
{
mStreamId->set_id(id);
return true;
}
quint32 StreamBase::ordinal()
{
return mCore->ordinal();
}
bool StreamBase::setOrdinal(quint32 ordinal)
{
mCore->set_ordinal(ordinal);
return true;
}
bool StreamBase::isEnabled() const
{
return mCore->is_enabled();
}
bool StreamBase::setEnabled(bool flag)
{
mCore->set_is_enabled(flag);
return true;
}
const QString StreamBase::name() const
{
return QString().fromStdString(mCore->name());
}
bool StreamBase::setName(QString name)
{
mCore->set_name(name.toStdString());
return true;
}
StreamBase::FrameLengthMode StreamBase::lenMode() const
{
return (StreamBase::FrameLengthMode) mCore->len_mode();
}
bool StreamBase::setLenMode(FrameLengthMode lenMode)
{
mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode);
return true;
}
quint16 StreamBase::frameLen(int streamIndex) const
{
int pktLen;
// Decide a frame length based on length mode
switch(lenMode())
{
case OstProto::StreamCore::e_fl_fixed:
pktLen = mCore->frame_len();
break;
case OstProto::StreamCore::e_fl_inc:
pktLen = frameLenMin() + (streamIndex %
(frameLenMax() - frameLenMin() + 1));
break;
case OstProto::StreamCore::e_fl_dec:
pktLen = frameLenMax() - (streamIndex %
(frameLenMax() - frameLenMin() + 1));
break;
case OstProto::StreamCore::e_fl_random:
//! \todo (MED) This 'random' sequence is same across iterations
pktLen = 64; // to avoid the 'maybe used uninitialized' warning
qsrand(reinterpret_cast(this));
for (int i = 0; i <= streamIndex; i++)
pktLen = qrand();
pktLen = frameLenMin() + (pktLen %
(frameLenMax() - frameLenMin() + 1));
break;
default:
qWarning("Unhandled len mode %d. Using default 64",
lenMode());
pktLen = 64;
break;
}
return pktLen;
}
bool StreamBase::setFrameLen(quint16 frameLen)
{
mCore->set_frame_len(frameLen);
return true;
}
quint16 StreamBase::frameLenMin() const
{
return mCore->frame_len_min();
}
bool StreamBase::setFrameLenMin(quint16 frameLenMin)
{
mCore->set_frame_len_min(frameLenMin);
return true;
}
quint16 StreamBase::frameLenMax() const
{
return mCore->frame_len_max();
}
bool StreamBase::setFrameLenMax(quint16 frameLenMax)
{
mCore->set_frame_len_max(frameLenMax);
return true;
}
/*! Convenience Function */
quint16 StreamBase::frameLenAvg() const
{
quint16 avgFrameLen;
if (lenMode() == e_fl_fixed)
avgFrameLen = frameLen();
else
avgFrameLen = (frameLenMin() + frameLenMax())/2;
return avgFrameLen;
}
StreamBase::SendUnit StreamBase::sendUnit() const
{
return (StreamBase::SendUnit) mControl->unit();
}
bool StreamBase::setSendUnit(SendUnit sendUnit)
{
mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit);
return true;
}
StreamBase::SendMode StreamBase::sendMode() const
{
return (StreamBase::SendMode) mControl->mode();
}
bool StreamBase::setSendMode(SendMode sendMode)
{
mControl->set_mode(
(OstProto::StreamControl::SendMode) sendMode);
return true;
}
StreamBase::NextWhat StreamBase::nextWhat() const
{
return (StreamBase::NextWhat) mControl->next();
}
bool StreamBase::setNextWhat(NextWhat nextWhat)
{
mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat);
return true;
}
quint32 StreamBase::numPackets() const
{
return (quint32) mControl->num_packets();
}
bool StreamBase::setNumPackets(quint32 numPackets)
{
mControl->set_num_packets(numPackets);
return true;
}
quint32 StreamBase::numBursts() const
{
return (quint32) mControl->num_bursts();
}
bool StreamBase::setNumBursts(quint32 numBursts)
{
mControl->set_num_bursts(numBursts);
return true;
}
quint32 StreamBase::burstSize() const
{
return (quint32) mControl->packets_per_burst();
}
bool StreamBase::setBurstSize(quint32 packetsPerBurst)
{
mControl->set_packets_per_burst(packetsPerBurst);
return true;
}
double StreamBase::packetRate() const
{
return (double) mControl->packets_per_sec();
}
bool StreamBase::setPacketRate(double packetsPerSec)
{
mControl->set_packets_per_sec(packetsPerSec);
return true;
}
double StreamBase::burstRate() const
{
return (double) mControl->bursts_per_sec();
}
bool StreamBase::setBurstRate(double burstsPerSec)
{
mControl->set_bursts_per_sec(burstsPerSec);
return true;
}
/*! Convenience Function */
double StreamBase::averagePacketRate() const
{
double avgPacketRate = 0;
switch (sendUnit())
{
case e_su_bursts:
avgPacketRate = burstRate() * burstSize();
break;
case e_su_packets:
avgPacketRate = packetRate();
break;
default:
Q_ASSERT(false); // Unreachable!!
}
return avgPacketRate;
}
/*! Convenience Function */
bool StreamBase::setAveragePacketRate(double packetsPerSec)
{
switch (sendUnit())
{
case e_su_bursts:
setBurstRate(packetsPerSec/double(burstSize()));
break;
case e_su_packets:
setPacketRate(packetsPerSec);
break;
default:
Q_ASSERT(false); // Unreachable!!
}
return true;
}
bool StreamBase::isFrameVariable() const
{
ProtocolListIterator *iter;
iter = createProtocolListIterator();
while (iter->hasNext())
{
AbstractProtocol *proto;
proto = iter->next();
if (proto->isProtocolFrameValueVariable())
goto _exit;
}
delete iter;
return false;
_exit:
delete iter;
return true;
}
bool StreamBase::isFrameSizeVariable() const
{
ProtocolListIterator *iter;
iter = createProtocolListIterator();
while (iter->hasNext())
{
AbstractProtocol *proto;
proto = iter->next();
if (proto->isProtocolFrameSizeVariable())
goto _exit;
}
delete iter;
return false;
_exit:
delete iter;
return true;
}
int StreamBase::frameSizeVariableCount() const
{
int count = 1;
switch(lenMode())
{
case OstProto::StreamCore::e_fl_fixed:
break;
case OstProto::StreamCore::e_fl_inc:
case OstProto::StreamCore::e_fl_dec:
case OstProto::StreamCore::e_fl_random:
count = frameLenMax() - frameLenMin() + 1;
break;
default:
qWarning("%s: Unhandled len mode %d", __FUNCTION__, lenMode());
break;
}
return count;
}
int StreamBase::frameVariableCount() const
{
ProtocolListIterator *iter;
quint64 frameCount = 1;
iter = createProtocolListIterator();
while (iter->hasNext())
{
AbstractProtocol *proto;
int count;
proto = iter->next();
count = proto->protocolFrameVariableCount();
// correct count for mis-behaving protocols
if (count <= 0)
count = 1;
frameCount = AbstractProtocol::lcm(frameCount, count);
}
delete iter;
return AbstractProtocol::lcm(frameCount, frameSizeVariableCount());
}
// frameProtocolLength() returns the sum of all the individual protocol sizes
// which may be different from frameLen()
int StreamBase::frameProtocolLength(int frameIndex) const
{
int len = 0;
ProtocolListIterator *iter = createProtocolListIterator();
while (iter->hasNext())
{
AbstractProtocol *proto = iter->next();
len += proto->protocolFrameSize(frameIndex);
}
delete iter;
return len;
}
int StreamBase::frameCount() const
{
int count = 0;
switch (sendUnit())
{
case e_su_packets: count = numPackets(); break;
case e_su_bursts: count = numBursts() * burstSize(); break;
default: Q_ASSERT(false); // unreachable
}
return count;
}
// Returns packet length - if bufMaxSize < frameLen(), returns truncated
// length i.e. bufMaxSize
int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const
{
int maxSize, size, pktLen, len = 0;
pktLen = frameLen(frameIndex);
// pktLen is adjusted for CRC/FCS which will be added by the NIC
pktLen -= kFcsSize;
if (pktLen <= 0)
return 0;
maxSize = qMin(pktLen, bufMaxSize);
ProtocolListIterator *iter;
iter = createProtocolListIterator();
while (iter->hasNext())
{
AbstractProtocol *proto;
QByteArray ba;
proto = iter->next();
ba = proto->protocolFrameValue(frameIndex);
size = qMin(ba.size(), maxSize-len);
memcpy(buf+len, ba.constData(), size);
len += size;
if (len == maxSize)
break;
}
delete iter;
// Pad with zero, if required and if we have space
if (len < maxSize) {
size = maxSize-len;
memset(buf+len, 0, size);
len += size;
}
return len;
}
quint64 StreamBase::deviceMacAddress(int frameIndex) const
{
return getDeviceMacAddress(portId_, int(mStreamId->id()), frameIndex);
}
quint64 StreamBase::neighborMacAddress(int frameIndex) const
{
return getNeighborMacAddress(portId_, int(mStreamId->id()), frameIndex);
}
bool StreamBase::preflightCheck(QString &result) const
{
bool pass = true;
int count = isFrameSizeVariable() ? frameCount() : 1;
for (int i = 0; i < count; i++)
{
if (frameLen(i) < (frameProtocolLength(i) + kFcsSize))
{
result += QString("One or more frames may be truncated - "
"frame length should be at least %1.\n")
.arg(frameProtocolLength(i) + kFcsSize);
pass = false;
break;
}
}
for (int i = 0; i < count; i++)
{
if (frameLen(i) > 1522)
{
result += QString("Jumbo frames may be truncated or dropped "
"if not supported by the hardware\n");
pass = false;
break;
}
}
return pass;
}
bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2)
{
return stream1->ordinal() < stream2->ordinal() ? true : false;
}