// Include a header file from your module to test. #include "ns3/tsn-net-device.h" #include "ns3/tas.h" #include "ns3/cbs.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("TasTestSuite"); // Add a doxygen group for tests. // If you have more than one test, this should be in only one of them. /** * \defgroup Tas-tests Tests for tas * \ingroup tsn * \ingroup tests */ /** * \ingroup Tas-tests * Check if message crossed a point to point tsn channel with a TAS */ class TasBasicTestCase : public TestCase { public: TasBasicTestCase(uint64_t pktSize, uint64_t expected_receive, uint8_t pcp); virtual ~TasBasicTestCase(); 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 uint64_t m_pktSize; uint64_t m_expected_receive; uint8_t m_pcp; }; // Add some help text to this case to describe what it is intended to test TasBasicTestCase::TasBasicTestCase(uint64_t pktSize, uint64_t expected_receive, uint8_t pcp) : TestCase("Check if paquets cross a point to point ethernet channel, tsn net device and a TAS") { m_pktSize = pktSize; m_expected_receive = expected_receive; m_pcp = pcp; } // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself TasBasicTestCase::~TasBasicTestCase() { } void TasBasicTestCase::SendTx(Ptr p) { m_sent += p->GetSize(); } void TasBasicTestCase::ReceiveRx(Ptr p) { m_received += p->GetSize(); } // // This method is the pure virtual method from class TestCase that every // TestCase must implement // void TasBasicTestCase::DoRun() { //Create two nodes Ptr n0 = CreateObject(); Ptr n1 = CreateObject(); //Add perfect clock on each node n0->AddClock(CreateObject()); n1->AddClock(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 for each netDevice. net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); //Create and add 8 FIFOs per net device for (int i=0; i<8; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); } //Add two GCL entry on net0 and start TAS net0->AddGclEntry(Time(MilliSeconds(10)), 0); net0->AddGclEntry(Time(MilliSeconds(10)), 32); net0->StartTas(); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(1)); app0->SetAttribute("PayloadSize", UintegerValue(m_pktSize)); app0->SetAttribute("Period", TimeValue(Seconds(5))); app0->SetAttribute("VlanID", UintegerValue(1)); app0->SetAttribute("PCP", UintegerValue(m_pcp)); n0->AddApplication(app0); app0->SetStartTime(Seconds(0)); app0->SetStopTime(Seconds(5)); //Callback to trace the message being send and received net0->TraceConnectWithoutContext("MacTx", MakeCallback(&TasBasicTestCase::SendTx, this)); net1->TraceConnectWithoutContext("MacRx", MakeCallback(&TasBasicTestCase::ReceiveRx, this)); //Execute the simulation Simulator::Stop(Seconds(5)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_sent, m_pktSize+22, "All Packets have been sent"); NS_TEST_ASSERT_MSG_EQ(m_received, m_expected_receive, "All Packets have been received"); } /** * \ingroup Tas-tests * Check if message crossed a point to point tsn channel with a TAS according to guard band */ class TasGuardBandTestCase : public TestCase { public: TasGuardBandTestCase(uint64_t pktSize, uint64_t expected_receive, Tas::GuardBandModes guardBandMode); virtual ~TasGuardBandTestCase(); 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 uint64_t m_pktSize; uint64_t m_expected_receive; Tas::GuardBandModes m_guardBandMode; }; // Add some help text to this case to describe what it is intended to test TasGuardBandTestCase::TasGuardBandTestCase(uint64_t pktSize, uint64_t expected_receive, Tas::GuardBandModes guardBandMode) : TestCase("Check if paquets cross a point to point ethernet channel, tsn net device and a TAS according to guard band mode") { m_pktSize = pktSize; m_expected_receive = expected_receive; m_guardBandMode = guardBandMode; } // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself TasGuardBandTestCase::~TasGuardBandTestCase() { } void TasGuardBandTestCase::SendTx(Ptr p) { m_sent += p->GetSize(); } void TasGuardBandTestCase::ReceiveRx(Ptr p) { m_received += p->GetSize(); } // // This method is the pure virtual method from class TestCase that every // TestCase must implement // void TasGuardBandTestCase::DoRun() { //Create two nodes Ptr n0 = CreateObject(); Ptr n1 = CreateObject(); //Add perfect clock on each node n0->AddClock(CreateObject()); n1->AddClock(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 for each netDevice. net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); //Create and add 8 FIFOs per net device for (int i=0; i<8; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); } //Add two GCL entry on net0 and start TAS net0->AddGclEntry(Time(MilliSeconds(10)), 0); net0->AddGclEntry(Time(NanoSeconds(4336)), 32); net0->StartTas(); //Set Tas Guard Band Mode net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(m_guardBandMode)); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(1)); app0->SetAttribute("PayloadSize", UintegerValue(m_pktSize)); app0->SetAttribute("Period", TimeValue(Seconds(5))); app0->SetAttribute("VlanID", UintegerValue(1)); app0->SetAttribute("PCP", UintegerValue(5)); n0->AddApplication(app0); app0->SetStartTime(Seconds(0)); app0->SetStopTime(Seconds(5)); //Callback to trace the message being send and received net0->TraceConnectWithoutContext("MacTx", MakeCallback(&TasGuardBandTestCase::SendTx, this)); net1->TraceConnectWithoutContext("MacRx", MakeCallback(&TasGuardBandTestCase::ReceiveRx, this)); //Execute the simulation Simulator::Stop(Seconds(5)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_sent, m_pktSize+22, "All Packets have been sent"); NS_TEST_ASSERT_MSG_EQ(m_received, m_expected_receive, "All Packets have been received"); } /** * \ingroup Tas-tests * Check last message latency that crossed a point to point tsn channel with a TAS */ class TasLatencyTestCase : public TestCase { public: TasLatencyTestCase(uint64_t pktSize, uint64_t pcp, uint64_t offset, Time expected_latency, DataRate datarate); virtual ~TasLatencyTestCase(); private: void DoRun() override; void Latency(Ptr p); uint64_t m_pktSize; uint64_t m_pcp; uint64_t m_offset; Time m_expected_latency; Time m_latency = Time(0); DataRate m_datarate; }; // Add some help text to this case to describe what it is intended to test TasLatencyTestCase::TasLatencyTestCase(uint64_t pktSize, uint64_t pcp, uint64_t offset, Time expected_latency, DataRate datarate) : TestCase("Check last message latency that crossed a point to point tsn channel with a TAS") { m_pktSize = pktSize; m_pcp = pcp; m_offset = offset; m_expected_latency = expected_latency; m_datarate = datarate; } // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself TasLatencyTestCase::~TasLatencyTestCase() {} void TasLatencyTestCase::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 TasLatencyTestCase::DoRun() { //Create two nodes Ptr n0 = CreateObject(); Ptr n1 = CreateObject(); //Add perfect clock on each node n0->AddClock(CreateObject()); n1->AddClock(CreateObject()); //Create and add a netDevice to each node Ptr net0 = CreateObject(); net0->SetAttribute("DataRate", DataRateValue(m_datarate)); n0->AddDevice(net0); Ptr net1 = CreateObject(); net1->SetAttribute("DataRate", DataRateValue(m_datarate)); 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 for each netDevice. net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); //Create and add 8 FIFOs per net device for (int i=0; i<8; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); } //Add two GCL entry on net0 and start TAS net0->AddGclEntry(Time(MilliSeconds(10)), 0); net0->AddGclEntry(Time(MilliSeconds(10)), 1); net0->AddGclEntry(Time(MilliSeconds(5)), 32); net0->AddGclEntry(Time(MilliSeconds(5)), 16); net0->StartTas(); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(1)); app0->SetAttribute("PayloadSize", UintegerValue(m_pktSize)); app0->SetAttribute("Period", TimeValue(Seconds(5))); app0->SetAttribute("VlanID", UintegerValue(1)); app0->SetAttribute("PCP", UintegerValue(m_pcp)); app0->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset))); n0->AddApplication(app0); app0->SetStartTime(Seconds(0)); app0->SetStopTime(Seconds(5)); //Callback to trace the message latency net1->TraceConnectWithoutContext("Latency", MakeCallback(&TasLatencyTestCase::Latency, this)); //Execute the simulation Simulator::Stop(Seconds(5)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_latency, m_expected_latency, "Packet experience the expected latency"); } /** * \ingroup Tas-tests * Check last vlan1 message latency that crossed a point to point tsn channel with a TAS with * mulitple gate open at the same time */ class TasLatencyTestCase2 : public TestCase { public: TasLatencyTestCase2(uint64_t pcp1, uint64_t pcp2, uint64_t offset1, uint64_t offset2, int64_t expected_latency); virtual ~TasLatencyTestCase2(); private: void DoRun() override; void Latency(Ptr p); uint64_t m_pcp1; uint64_t m_pcp2; uint64_t m_offset1; uint64_t m_offset2; int64_t m_expected_latency; Time m_latency = Time(0); }; // Add some help text to this case to describe what it is intended to test TasLatencyTestCase2::TasLatencyTestCase2(uint64_t pcp1, uint64_t pcp2, uint64_t offset1, uint64_t offset2, int64_t expected_latency) : TestCase("Check last vlan1 message latency that crossed a point to point tsn channel with a TAS with flow at the same time") { m_pcp1 = pcp1; m_pcp2 = pcp2; m_offset1 = offset1; m_offset2 = offset2; m_expected_latency = expected_latency; } // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself TasLatencyTestCase2::~TasLatencyTestCase2() {} void TasLatencyTestCase2::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 TasLatencyTestCase2::DoRun() { //Create two nodes Ptr n0 = CreateObject(); Ptr n1 = CreateObject(); //Add perfect clock on each node n0->AddClock(CreateObject()); n1->AddClock(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 for each netDevice. net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); //Create and add 8 FIFOs per net device for (int i=0; i<8; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); } //Add two GCL entry on net0 and start TAS net0->AddGclEntry(Time(MilliSeconds(10)), 32); net0->AddGclEntry(Time(MilliSeconds(10)), 0); net0->AddGclEntry(Time(MilliSeconds(5)), 8); net0->AddGclEntry(Time(MilliSeconds(5)), 5); net0->StartTas(); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(1)); app0->SetAttribute("PayloadSize", UintegerValue(500)); app0->SetAttribute("Period", TimeValue(Seconds(5))); app0->SetAttribute("VlanID", UintegerValue(1)); app0->SetAttribute("PCP", UintegerValue(m_pcp2)); app0->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset1))); n0->AddApplication(app0); app0->SetStartTime(Seconds(0)); app0->SetStopTime(MilliSeconds(30)); Ptr app1 = CreateObject(); app1->Setup(net0); app1->SetAttribute("BurstSize", UintegerValue(1)); app1->SetAttribute("PayloadSize", UintegerValue(1000)); app1->SetAttribute("Period", TimeValue(Seconds(5))); app1->SetAttribute("VlanID", UintegerValue(2)); app1->SetAttribute("PCP", UintegerValue(m_pcp2)); app1->SetAttribute("Offset", TimeValue(NanoSeconds(m_offset2))); n0->AddApplication(app1); app1->SetStartTime(Seconds(0)); app1->SetStopTime(MilliSeconds(30)); //Callback to trace the message latency net1->TraceConnectWithoutContext("Latency", MakeCallback(&TasLatencyTestCase2::Latency, this)); //Execute the simulation Simulator::Stop(MilliSeconds(40)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_expected_latency, "Packet experience the expected latency"); } /** * \ingroup Tas-tests * Check last message latency that crossed a point to point tsn channel with a TAS */ class TasCbsLatencyTestCase : public TestCase { public: TasCbsLatencyTestCase(int64_t expected_latency); virtual ~TasCbsLatencyTestCase(); private: void DoRun() override; void Latency(Ptr p); int64_t m_expected_latency; Time m_latency = Time(0); }; // Add some help text to this case to describe what it is intended to test TasCbsLatencyTestCase::TasCbsLatencyTestCase(int64_t expected_latency) : TestCase("Check last message latency that crossed a point to point tsn channel with a TAS and CBS") { m_expected_latency = expected_latency; } // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself TasCbsLatencyTestCase::~TasCbsLatencyTestCase() {} void TasCbsLatencyTestCase::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 TasCbsLatencyTestCase::DoRun() { //Create two nodes Ptr n0 = CreateObject(); Ptr n1 = CreateObject(); //Add perfect clock on each node n0->AddClock(CreateObject()); n1->AddClock(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 for each netDevice. net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); //Create and add 8 FIFOs per net device for (int i=0; i<7; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); } Ptr cbs = CreateObject(); cbs->SetTsnNetDevice(net0); cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("5Mb/s"))); cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("1Gb/s"))); net0->SetQueue(CreateObject>(), cbs); net1->SetQueue(CreateObject>()); //Add two GCL entry on net0 and start TAS net0->AddGclEntry(Time(MilliSeconds(3)), 128); net0->AddGclEntry(Time(MilliSeconds(17)), 0); net0->AddGclEntry(Time(MilliSeconds(3)), 128); net0->AddGclEntry(Time(MilliSeconds(7)), 0); net0->StartTas(); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(3)); app0->SetAttribute("PayloadSize", UintegerValue(1378)); app0->SetAttribute("Period", TimeValue(Seconds(5))); app0->SetAttribute("VlanID", UintegerValue(1)); app0->SetAttribute("PCP", UintegerValue(7)); n0->AddApplication(app0); app0->SetStartTime(Seconds(0)); app0->SetStopTime(MilliSeconds(3)); //Callback to trace the message latency net1->TraceConnectWithoutContext("Latency", MakeCallback(&TasCbsLatencyTestCase::Latency, this)); //Execute the simulation Simulator::Stop(Seconds(5)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_latency.GetNanoSeconds(), m_expected_latency, "Packet experience the expected 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 tas-tests * TestSuite for module tas */ class TasTestSuite : public TestSuite { public: TasTestSuite(); }; TasTestSuite::TasTestSuite() : TestSuite("tas", UNIT) { LogComponentEnable("TasTestSuite", LOG_LEVEL_ALL); LogComponentEnable("Tas", LOG_LEVEL_ALL); //Point to point network with tas uint64_t pktSize = 500; //Pkt crossed the link with TAS AddTestCase(new TasBasicTestCase(pktSize, pktSize + 22, 5), TestCase::QUICK); AddTestCase(new TasBasicTestCase(pktSize, 0, 4), TestCase::QUICK); //Pkt crossed the link with TAS or not according to GuardBandMode AddTestCase(new TasGuardBandTestCase(pktSize, 0, Tas::MTU), TestCase::QUICK); AddTestCase(new TasGuardBandTestCase(pktSize, pktSize + 22, Tas::PKTSIZE), TestCase::QUICK); //Latency //Pkt ready before gate open AddTestCase(new TasLatencyTestCase(pktSize, 5, 0, Time(NanoSeconds(20*pow(10,6) + (pktSize + 22 + 8)*8 +25)), DataRate("1Gb/s")), TestCase::QUICK); AddTestCase(new TasLatencyTestCase(pktSize, 5, 18*pow(10,6), Time(NanoSeconds(2*pow(10,6) + (pktSize + 22 + 8)*8 +25)), DataRate("1Gb/s")), TestCase::QUICK); //Pkt ready when gate open AddTestCase(new TasLatencyTestCase(pktSize, 5, 20*pow(10,6), Time(NanoSeconds((pktSize + 22 + 8)*8 +25)), DataRate("1Gb/s")), TestCase::QUICK); //Pkt ready after gate open AddTestCase(new TasLatencyTestCase(pktSize, 5, 22*pow(10,6), Time(NanoSeconds((pktSize + 22 + 8)*8 +25)), DataRate("1Gb/s")), TestCase::QUICK); //Pkt never transmitted because his gate never open AddTestCase(new TasLatencyTestCase(pktSize, 7, 0, Time(NanoSeconds(0)), DataRate("1Gb/s")), TestCase::QUICK); //MultiGig if(Time::GetResolution()==Time::PS){ AddTestCase(new TasLatencyTestCase(pktSize, 5, 25*pow(10,6) - 1500*3.2, Time(NanoSeconds((pktSize + 22 + 8)*3.2 +25)), DataRate("2.5Gb/s")), TestCase::QUICK); //Send at window closing - MTU duration AddTestCase(new TasLatencyTestCase(pktSize, 5, 25*pow(10,6) - 1500*1.6, Time(NanoSeconds((pktSize + 22 + 8)*1.6 +25)), DataRate("5Gb/s")), TestCase::QUICK); //Send at window closing - MTU duration AddTestCase(new TasLatencyTestCase(pktSize, 5, 25*pow(10,6) - 1500*0.8, Time(NanoSeconds((pktSize + 22 + 8)*0.8 +25)), DataRate("10Gb/s")), TestCase::QUICK); //Send at window closing - MTU duration } //Latency TAS with two flow on same gate AddTestCase(new TasLatencyTestCase2(5, 5, 1, 0, (1000 + 22 + 8 + 12 + 500 + 22 +8)*8+ 25 - 1), TestCase::QUICK); AddTestCase(new TasLatencyTestCase2(5, 5, 0, 0, (500 + 22 +8)*8+ 25), TestCase::QUICK); AddTestCase(new TasLatencyTestCase2(5, 5, 0, 1, (500 + 22 +8)*8+ 25), TestCase::QUICK); //Latency TAS with multiple gate open at the same time AddTestCase(new TasLatencyTestCase2(2, 0, 0, 0, 25*pow(10,6) + (500 + 22 +8)*8+ 25), TestCase::QUICK); AddTestCase(new TasLatencyTestCase2(2, 0, 25*pow(10,6)+1, 0, (1000 + 22 + 8 + 12 + 500 + 22 +8)*8+ 25 -1), TestCase::QUICK); //Latency TAS + CBS //the last pkt need to wait the second gate opening to be transmitted AddTestCase(new TasCbsLatencyTestCase(21555289), TestCase::QUICK); } // Do not forget to allocate an instance of this TestSuite /** * \ingroup tas-tests * Static variable for test initialization */ static TasTestSuite m_tasTestSuite;