// 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 p); void ReceiveRx(Ptr 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 p) { m_sent += p->GetSize(); } void TsnBasicTestCase::ReceiveRx(Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); //Create and add a netDevice to each node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); //Create a Tsn Channel and attach it two the two netDevices Ptr channel = CreateObject(); 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>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); //Application description Ptr app0 = CreateObject(); 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 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 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 n0 = CreateObject(); Ptr n1 = CreateObject(); //Create and add a netDevice to each node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); //Create a Tsn Channel and attach it two the two netDevices Ptr channel = CreateObject(); 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>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); //Application description Ptr app0 = CreateObject(); 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 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 p) { TimestampTag tag; if (!p->FindFirstMatchingByteTag(tag)) { return; } Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); //Create and add a netDevice to each node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); //Create a Tsn Channel and attach it two the two netDevices Ptr channel = CreateObject(); 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>()); net0->SetQueue(CreateObject>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); //Application descriptions Ptr app0 = CreateObject(); 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 app1 = CreateObject(); 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 p); void ReceiveRx(Ptr 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 p) { m_sent += p->GetSize(); } void TsnSwtichBasicTestCase::ReceiveRx(Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); Ptr n2 = CreateObject(); //Create and add a netDevice to each end station node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); //Create and add a netDevice to each switch port Ptr swnet0 = CreateObject(); n2->AddDevice(swnet0); Ptr swnet1 = CreateObject(); n2->AddDevice(swnet1); //Create Tsn Channels and connect switch to the end-stations Ptr channel0 = CreateObject(); net0->Attach(channel0); swnet0->Attach(channel0); Ptr channel1 = CreateObject(); net1->Attach(channel1); swnet1->Attach(channel1); //Create and add a switch net device to the switch node Ptr sw = CreateObject(); 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>()); net0->SetQueue(CreateObject>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); sw->SetAddress(Mac48Address::Allocate()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); //Add forwarding table sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 1, {swnet1}); //Application descriptions Ptr app0 = CreateObject(); 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 p); void ReceiveRx(Ptr 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 p) { m_sent += p->GetSize(); } void TsnSwtichMulticastTestCase::ReceiveRx(Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); Ptr n2 = CreateObject(); Ptr n3 = CreateObject(); //Create and add a netDevice to each end station node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); Ptr net2 = CreateObject(); n2->AddDevice(net2); //Create and add a netDevice to each switch port Ptr swnet0 = CreateObject(); n3->AddDevice(swnet0); Ptr swnet1 = CreateObject(); n3->AddDevice(swnet1); Ptr swnet2 = CreateObject(); n3->AddDevice(swnet2); //Create Tsn Channels and connect switch to the end-stations Ptr channel0 = CreateObject(); net0->Attach(channel0); swnet0->Attach(channel0); Ptr channel1 = CreateObject(); net1->Attach(channel1); swnet1->Attach(channel1); Ptr channel2 = CreateObject(); net2->Attach(channel2); swnet2->Attach(channel2); //Create and add a switch net device to the switch node Ptr sw = CreateObject(); 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>()); net0->SetQueue(CreateObject>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); sw->SetAddress(Mac48Address::Allocate()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet2->SetQueue(CreateObject>()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet2->SetQueue(CreateObject>()); //Add forwarding table sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1, swnet2}); //Application descriptions Ptr app0 = CreateObject(); 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 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 p) { TimestampTag tag; if (!p->FindFirstMatchingByteTag(tag)) { return; } Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); Ptr n2 = CreateObject(); //Create and add a netDevice to each end station node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); //Create and add a netDevice to each switch port Ptr swnet0 = CreateObject(); n2->AddDevice(swnet0); Ptr swnet1 = CreateObject(); n2->AddDevice(swnet1); //Create Tsn Channels and connect switch to the end-stations Ptr channel0 = CreateObject(); net0->Attach(channel0); swnet0->Attach(channel0); Ptr channel1 = CreateObject(); net1->Attach(channel1); swnet1->Attach(channel1); //Create and add a switch net device to the switch node Ptr sw = CreateObject(); 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>()); net0->SetQueue(CreateObject>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); sw->SetAddress(Mac48Address::Allocate()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); //Add forwarding table sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1}); //Application descriptions Ptr app0 = CreateObject(); 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 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 p) { TimestampTag tag; if (!p->FindFirstMatchingByteTag(tag)) { return; } Ptr 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 n0 = CreateObject(); Ptr n1 = CreateObject(); Ptr n2 = CreateObject(); Ptr n3 = CreateObject(); //Create and add a netDevice to each end station node Ptr net0 = CreateObject(); n0->AddDevice(net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); Ptr net2 = CreateObject(); n2->AddDevice(net2); //Create and add a netDevice to each switch port Ptr swnet0 = CreateObject(); n3->AddDevice(swnet0); Ptr swnet1 = CreateObject(); n3->AddDevice(swnet1); Ptr swnet2 = CreateObject(); n3->AddDevice(swnet2); //Create Tsn Channels and connect switch to the end-stations Ptr channel0 = CreateObject(); net0->Attach(channel0); swnet0->Attach(channel0); Ptr channel1 = CreateObject(); net1->Attach(channel1); swnet1->Attach(channel1); Ptr channel2 = CreateObject(); net2->Attach(channel2); swnet2->Attach(channel2); //Create and add a switch net device to the switch node Ptr sw = CreateObject(); 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>()); net0->SetQueue(CreateObject>()); net1->SetAddress(Mac48Address::Allocate()); net1->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); sw->SetAddress(Mac48Address::Allocate()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet2->SetQueue(CreateObject>()); swnet0->SetQueue(CreateObject>()); swnet1->SetQueue(CreateObject>()); swnet2->SetQueue(CreateObject>()); //Add forwarding table sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 1, {swnet1, swnet2}); //Application descriptions Ptr app0 = CreateObject(); 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 app1 = CreateObject(); 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;