Files
eden-sim/contrib/tsn/test/tsn-test-suite.cc

1013 lines
36 KiB
C++
Raw Permalink Normal View History

2025-12-01 15:56:02 +01:00
// Include a header file from your module to test.
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-generator.h"
#include "ns3/switch-net-device.h"
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/core-module.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("TsnTestSuite");
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup tsn-tests Tests for tsn
* \ingroup tsn
* \ingroup tests
*/
/**
* \ingroup tsn-tests
* Check if message crossed a point to point tsn channel
*/
class TsnBasicTestCase : public TestCase
{
public:
TsnBasicTestCase();
virtual ~TsnBasicTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
TsnBasicTestCase::TsnBasicTestCase()
: TestCase("Check if paquets cross a point to point ethernet channel and tsn net device")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnBasicTestCase::~TsnBasicTestCase()
{
}
void
TsnBasicTestCase::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
TsnBasicTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnBasicTestCase::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address and create a FIFO (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&TsnBasicTestCase::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnBasicTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_sent, m_received, "All Packets sent have been received");
}
/**
* \ingroup tsn-tests
* Check if latency are correct with 1 flow
*/
class TsnLatencyTestCase1 : public TestCase
{
public:
TsnLatencyTestCase1(uint16_t burstSize, int64_t latency);
virtual ~TsnLatencyTestCase1();
private:
void DoRun() override;
void Latency(Ptr<const Packet> p);
Time m_latency{0};
uint16_t m_burstSize;
int64_t m_true_latency;
};
// Add some help text to this case to describe what it is intended to test
TsnLatencyTestCase1::TsnLatencyTestCase1(uint16_t burstSize, int64_t latency)
: TestCase("Check if latency are correct on a point to point ethernet channel and tsn net device with one flow")
{
m_burstSize = burstSize;
m_true_latency = latency;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnLatencyTestCase1::~TsnLatencyTestCase1()
{
}
void
TsnLatencyTestCase1::Latency(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnLatencyTestCase1::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address and create a FIFO (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burstSize));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(1));
//Callback to trace the latency
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnLatencyTestCase1::Latency, this));
//Execute the simulation
Simulator::Stop(Seconds(1));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_true_latency, "Packet experience the correct latency");
}
/**
* \ingroup tsn-tests
* Check if latency are correct with 2 flow
*/
class TsnLatencyTestCase2 : public TestCase
{
public:
TsnLatencyTestCase2(uint16_t burstSizeApp0, uint16_t burstSizeApp1, uint16_t offset0, uint16_t offset1, uint8_t pcpApp0, uint8_t pcpApp1, int64_t latency);
virtual ~TsnLatencyTestCase2();
private:
void DoRun() override;
void Latency(Ptr<const Packet> p);
Time m_latency{0};
uint16_t m_burstSizeApp0;
uint16_t m_burstSizeApp1;
uint16_t m_offset0;
uint16_t m_offset1;
uint8_t m_pcpApp0;
uint8_t m_pcpApp1;
int64_t m_true_latency;
};
// Add some help text to this case to describe what it is intended to test
TsnLatencyTestCase2::TsnLatencyTestCase2(uint16_t burstSizeApp0, uint16_t burstSizeApp1, uint16_t offset0, uint16_t offset1, uint8_t pcpApp0, uint8_t pcpApp1, int64_t latency)
: TestCase("Check if latency are correct on a point to point ethernet channel and tsn net device with two flows")
{
m_burstSizeApp0 = burstSizeApp0;
m_burstSizeApp1 = burstSizeApp1;
m_offset0 = offset0;
m_offset1 = offset1;
m_pcpApp0 = pcpApp0;
m_pcpApp1 = pcpApp1;
m_true_latency = latency;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnLatencyTestCase2::~TsnLatencyTestCase2()
{
}
void
TsnLatencyTestCase2::Latency(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
if (ethHeader.GetVid()!=1)
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnLatencyTestCase2::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address and create a FIFO (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application descriptions
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burstSizeApp0));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset0)));
app0->SetAttribute("PCP", UintegerValue(m_pcpApp0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(1));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(m_burstSizeApp1));
app1->SetAttribute("PayloadSize", UintegerValue(100));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset1)));
app1->SetAttribute("PCP", UintegerValue(m_pcpApp1));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(1));
//Callback to trace the latency
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnLatencyTestCase2::Latency, this));
//Execute the simulation
Simulator::Stop(Seconds(1));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_true_latency, "Packet experience the correct latency");
}
/**
* \ingroup tsn-tests
* Check if message crossed a tsn switch
*/
class TsnSwtichBasicTestCase : public TestCase
{
public:
TsnSwtichBasicTestCase();
virtual ~TsnSwtichBasicTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
TsnSwtichBasicTestCase::TsnSwtichBasicTestCase()
: TestCase("Check if paquets cross an tsn switch")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnSwtichBasicTestCase::~TsnSwtichBasicTestCase()
{
}
void
TsnSwtichBasicTestCase::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
TsnSwtichBasicTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnSwtichBasicTestCase::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
//Create and add a netDevice to each end station node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create and add a netDevice to each switch port
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n2->AddDevice(swnet0);
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
n2->AddDevice(swnet1);
//Create Tsn Channels and connect switch to the end-stations
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
net0->Attach(channel0);
swnet0->Attach(channel0);
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
net1->Attach(channel1);
swnet1->Attach(channel1);
//Create and add a switch net device to the switch node
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n2->AddDevice(sw);
sw->AddSwitchPort(swnet0);
sw->AddSwitchPort(swnet1);
//Allocate a Mac address and create 2 FIFOs (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
sw->SetAddress(Mac48Address::Allocate());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add forwarding table
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 1, {swnet1});
//Application descriptions
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net1->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(2.5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(5));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&TsnSwtichBasicTestCase::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnSwtichBasicTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_sent, m_received, "All Packets sent have been received");
}
/**
* \ingroup tsn-tests
* Check if multicast message crossed a tsn switch
*/
class TsnSwtichMulticastTestCase : public TestCase
{
public:
TsnSwtichMulticastTestCase();
virtual ~TsnSwtichMulticastTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
TsnSwtichMulticastTestCase::TsnSwtichMulticastTestCase()
: TestCase("Check if multicast paquets cross an tsn switch")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnSwtichMulticastTestCase::~TsnSwtichMulticastTestCase()
{
}
void
TsnSwtichMulticastTestCase::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
TsnSwtichMulticastTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnSwtichMulticastTestCase::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each end station node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
//Create and add a netDevice to each switch port
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet0);
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet1);
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet2);
//Create Tsn Channels and connect switch to the end-stations
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
net0->Attach(channel0);
swnet0->Attach(channel0);
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
net1->Attach(channel1);
swnet1->Attach(channel1);
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
net2->Attach(channel2);
swnet2->Attach(channel2);
//Create and add a switch net device to the switch node
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw);
sw->AddSwitchPort(swnet0);
sw->AddSwitchPort(swnet1);
sw->AddSwitchPort(swnet2);
//Allocate a Mac address and create 2 FIFOs (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
sw->SetAddress(Mac48Address::Allocate());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add forwarding table
sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1, swnet2});
//Application descriptions
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
app0->SetAttribute("BurstSize", UintegerValue(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(2.5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(5));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&TsnSwtichMulticastTestCase::SendTx, this));
net0->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnSwtichMulticastTestCase::ReceiveRx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnSwtichMulticastTestCase::ReceiveRx, this));
net2->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnSwtichMulticastTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_sent * 2, m_received, "All Packets sent have been received on the two destination");
}
/**
* \ingroup tsn-tests
* Check if latency are correct in a tsn switched network with one flow
*/
class TsnSwitchLatencyTestCase1 : public TestCase
{
public:
TsnSwitchLatencyTestCase1(uint16_t burstSizeApp0, int64_t latency);
virtual ~TsnSwitchLatencyTestCase1();
private:
void DoRun() override;
void Latency(Ptr<const Packet> p);
Time m_latency{0};
uint16_t m_burstSizeApp0;
int64_t m_true_latency;
};
// Add some help text to this case to describe what it is intended to test
TsnSwitchLatencyTestCase1::TsnSwitchLatencyTestCase1(uint16_t burstSizeApp0, int64_t latency)
: TestCase("Check if latency are correct in a tsn switched network with one flow")
{
m_burstSizeApp0 = burstSizeApp0;
m_true_latency = latency;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnSwitchLatencyTestCase1::~TsnSwitchLatencyTestCase1()
{
}
void
TsnSwitchLatencyTestCase1::Latency(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
if (ethHeader.GetVid()!=1)
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnSwitchLatencyTestCase1::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
//Create and add a netDevice to each end station node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create and add a netDevice to each switch port
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n2->AddDevice(swnet0);
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
n2->AddDevice(swnet1);
//Create Tsn Channels and connect switch to the end-stations
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
net0->Attach(channel0);
swnet0->Attach(channel0);
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
net1->Attach(channel1);
swnet1->Attach(channel1);
//Create and add a switch net device to the switch node
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n2->AddDevice(sw);
sw->AddSwitchPort(swnet0);
sw->AddSwitchPort(swnet1);
//Allocate a Mac address and create 2 FIFOs (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
sw->SetAddress(Mac48Address::Allocate());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add forwarding table
sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1});
//Application descriptions
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burstSizeApp0));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(1));
//Callback to compute the packet latency
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnSwitchLatencyTestCase1::Latency, this));
//Execute the simulation
Simulator::Stop(Seconds(2));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_true_latency, "Packet experience the correct latency");
}
/**
* \ingroup tsn-tests
* Check if latency are correct in a tsn switched network with 2 flow
*/
class TsnSwitchLatencyTestCase2 : public TestCase
{
public:
TsnSwitchLatencyTestCase2(uint16_t burstSizeApp0, uint16_t burstSizeApp1, uint16_t offset0, uint16_t offset1, uint8_t pcpApp0, uint8_t pcpApp1, int64_t latency);
virtual ~TsnSwitchLatencyTestCase2();
private:
void DoRun() override;
void Latency(Ptr<const Packet> p);
Time m_latency{0};
uint16_t m_burstSizeApp0;
uint16_t m_burstSizeApp1;
uint16_t m_offset0;
uint16_t m_offset1;
uint8_t m_pcpApp0;
uint8_t m_pcpApp1;
int64_t m_true_latency;
};
// Add some help text to this case to describe what it is intended to test
TsnSwitchLatencyTestCase2::TsnSwitchLatencyTestCase2(uint16_t burstSizeApp0, uint16_t burstSizeApp1, uint16_t offset0, uint16_t offset1, uint8_t pcpApp0, uint8_t pcpApp1, int64_t latency)
: TestCase("Check if latency are correct in a tsn switched network with two flows")
{
m_burstSizeApp0 = burstSizeApp0;
m_burstSizeApp1 = burstSizeApp1;
m_offset0 = offset0;
m_offset1 = offset1;
m_pcpApp0 = pcpApp0;
m_pcpApp1 = pcpApp1;
m_true_latency = latency;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnSwitchLatencyTestCase2::~TsnSwitchLatencyTestCase2()
{
}
void
TsnSwitchLatencyTestCase2::Latency(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
if (ethHeader.GetVid()!=1)
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnSwitchLatencyTestCase2::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each end station node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
//Create and add a netDevice to each switch port
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet0);
Ptr<TsnNetDevice> swnet1 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet1);
Ptr<TsnNetDevice> swnet2 = CreateObject<TsnNetDevice>();
n3->AddDevice(swnet2);
//Create Tsn Channels and connect switch to the end-stations
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
net0->Attach(channel0);
swnet0->Attach(channel0);
Ptr<EthernetChannel> channel1 = CreateObject<EthernetChannel>();
net1->Attach(channel1);
swnet1->Attach(channel1);
Ptr<EthernetChannel> channel2 = CreateObject<EthernetChannel>();
net2->Attach(channel2);
swnet2->Attach(channel2);
//Create and add a switch net device to the switch node
Ptr<SwitchNetDevice> sw = CreateObject<SwitchNetDevice>();
sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw);
sw->AddSwitchPort(swnet0);
sw->AddSwitchPort(swnet1);
sw->AddSwitchPort(swnet2);
//Allocate a Mac address and create 2 FIFOs (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
sw->SetAddress(Mac48Address::Allocate());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet2->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add forwarding table
sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1, swnet2});
//Application descriptions
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burstSizeApp0));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset0)));
app0->SetAttribute("PCP", UintegerValue(m_pcpApp0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(1));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(m_burstSizeApp1));
app1->SetAttribute("PayloadSize", UintegerValue(100));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset1)));
app1->SetAttribute("PCP", UintegerValue(m_pcpApp1));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(1));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnSwitchLatencyTestCase2::Latency, this));
net0->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnSwitchLatencyTestCase2::Latency, this));
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnSwitchLatencyTestCase2::Latency, this));
net2->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnSwitchLatencyTestCase2::Latency, this));
//Execute the simulation
Simulator::Stop(Seconds(2));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_true_latency, "Packet experience the correct latency");
}
// The TestSuite class names the TestSuite, identifies what type of TestSuite,
// and enables the TestCases to be run. Typically, only the constructor for
// this class must be defined
/**
* \ingroup tsn-tests
* TestSuite for module tsn
*/
class TsnTestSuite : public TestSuite
{
public:
TsnTestSuite();
};
TsnTestSuite::TsnTestSuite()
: TestSuite("tsn", UNIT)
{
LogComponentEnable("TsnTestSuite", LOG_LEVEL_ALL);
//Point to point network
AddTestCase(new TsnBasicTestCase, TestCase::QUICK);
//Latency test with one flow
AddTestCase(new TsnLatencyTestCase1(1, (1400 + 22 + 8) * 8 + 25), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase1(5, ((1400 + 22 + 8 + 12)*5 - 12) * 8 + 25), TestCase::QUICK);
//Latency test with 2 flows
//Flow under study have higher priority
AddTestCase(new TsnLatencyTestCase2(1,1,0,0,1,0, ((1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(2,1,0,0,1,0, ((1400+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(1,2,0,0,1,0, ((1400+22+8) * 8 + 25)), TestCase::QUICK);
//Flow under study have higher priority but a packet is already being sent
AddTestCase(new TsnLatencyTestCase2(1,1,1,0,1,0, ((100+22+8+12 + 1400+22+8) * 8 + 25 - 1)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(2,1,1,0,1,0, ((100+22+8+12 + 1400+22+8+12 + 1400+22+8) * 8 + 25 - 1)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(1,2,1,0,1,0, ((100+22+8+12 + 1400+22+8) * 8 + 25 - 1)), TestCase::QUICK);
//Flow under study have lower priority
AddTestCase(new TsnLatencyTestCase2(1,1,0,0,0,1, ((100+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(2,1,0,0,0,1, ((100+22+8+12+ 1400+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(1,2,0,0,0,1, ((100+22+8+12+ 100+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(2,2,0,1,0,1, ((1400+22+8+12 + 100+22+8+12 + 100+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
//Flow under study is alone
AddTestCase(new TsnLatencyTestCase2(1,1,0,1,1,0, ((1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(2,1,0,1,1,0, ((1400+22+8+12 + 1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnLatencyTestCase2(1,2,0,1,1,0, ((1400+22+8) * 8 + 25)), TestCase::QUICK);
//Switched network
AddTestCase(new TsnSwtichBasicTestCase, TestCase::QUICK);
AddTestCase(new TsnSwtichMulticastTestCase, TestCase::QUICK);
//Latency test with one flow
AddTestCase(new TsnSwitchLatencyTestCase1(1, (1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase1(5, ((1400+22+8+12)*5-12)*8 + 25 + 10000 + (1400+22+8)*8 + 25), TestCase::QUICK);
//Latency test with 2 flows
//Flow under study have higher priority
AddTestCase(new TsnSwitchLatencyTestCase2(1,1,0,0,1,0, ((1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(2,1,0,0,1,0, ((1400+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(1,2,0,0,1,0, ((1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
//Flow under study have higher priority but a packet is already being sent
AddTestCase(new TsnSwitchLatencyTestCase2(1,1,1,0,1,0, ((100+22+8+12 + 1400+22+8) * 8 - 1 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(2,1,1,0,1,0, ((100+22+8+12 + 1400+22+8+12 + 1400+22+8) * 8 - 1 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(1,2,1,0,1,0, ((100+22+8+12 + 1400+22+8) * 8 - 1 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
//Flow under study have lower priority
AddTestCase(new TsnSwitchLatencyTestCase2(1,1,0,0,0,1, ((100+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(2,1,0,0,0,1, ((100+22+8+12 + 1400+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(1,2,0,0,0,1, ((100+22+8+12 + 100+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(2,2,0,1,0,1, ((100+22+8+12 + 100+22+8+12 + 1400+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
//Flow under study is alone
AddTestCase(new TsnSwitchLatencyTestCase2(1,1,0,1,1,0, ((1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(2,1,0,1,1,0, ((1400+22+8+12 + 1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
AddTestCase(new TsnSwitchLatencyTestCase2(1,2,0,1,1,0, ((1400+22+8) * 8 + 25 + 10000 + (1400+22+8) * 8 + 25)), TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup tsn-tests
* Static variable for test initialization
*/
static TsnTestSuite m_tsnTestSuite;