// Include a header file from your module to test. #include "ns3/tsn-node.h" #include "ns3/tsn-net-device.h" #include "ns3/switch-net-device.h" #include "ns3/ethernet-channel.h" #include "ns3/ethernet-generator.h" #include "ns3/ethernet-header2.h" #include "ns3/stream-identification-function.h" #include "ns3/stream-identification-function-null.h" #include "ns3/frer-sequence-generation-function.h" #include "ns3/frer-sequence-recovery-function.h" #include "ns3/frer-latent-error-detection-function.h" #include "ns3/frer-match-recovery-function.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("FRERTestSuite"); // Add a doxygen group for tests. // If you have more than one test, this should be in only one of them. /** * \defgroup FRER-tests Tests for FRER * \ingroup tsn * \ingroup tests */ /** * \ingroup FRER-tests * Check if paquets are correctly replicated and eliminated on a 4 SW / 2 ES network * * / ==== SW2 ==== \ * ESsource ==== SW1 SW4 ==== ESdest * \ ==== SW3 ==== / * * The FRER use in this test use the SW1 to do the replication * using mulitcast and SW4 to do the elimination. The stream identification is * done by the null stream identification function. The match recovery function, * latent error detection function are also used in this example. * Two flows go from ESsource to ESdest. Only the VLAN 100 flow use FRER mechanisms. * * */ class FRERBasicMulticastTestCase : public TestCase { public: FRERBasicMulticastTestCase(); virtual ~FRERBasicMulticastTestCase(); private: void DoRun() override; void SendTx(Ptr p); void ReceiveRx(Ptr p); uint64_t m_replicated{0}; //!< number of bytes sent uint64_t m_received{0}; //!< number of bytes received uint64_t m_pktSize = 500; uint64_t m_expected_replicated = (1400 + 22 + 6) * 2 + (500+22); //the 1400Bytes pkt is replicated two time and RTAGed (+6bits), the 500Bytes pkt is not replicated uint64_t m_expected_receive = (1400 + 22) + (500+22); }; // Add some help text to this case to describe what it is intended to test FRERBasicMulticastTestCase::FRERBasicMulticastTestCase() : TestCase("Check if paquets are correctly replicated and eliminated") {} // This destructor does nothing but we include it as a reminder that // the test case should clean up after itself FRERBasicMulticastTestCase::~FRERBasicMulticastTestCase() { } void FRERBasicMulticastTestCase::SendTx(Ptr p) { m_replicated += p->GetSize(); } void FRERBasicMulticastTestCase::ReceiveRx(Ptr p) { m_received += p->GetSize(); } // // This method is the pure virtual method from class TestCase that every // TestCase must implement // void FRERBasicMulticastTestCase::DoRun() { //Create six nodes Ptr n0 = CreateObject(); Names::Add("ESsource", n0); Ptr n1 = CreateObject(); Names::Add("ESdest", n1); Ptr n2 = CreateObject(); Names::Add("SW1", n2); Ptr n3 = CreateObject(); Names::Add("SW2", n3); Ptr n4 = CreateObject(); Names::Add("SW3", n4); Ptr n5 = CreateObject(); Names::Add("SW4", n5); //Create and add a netDevices to each node Ptr net0 = CreateObject(); n0->AddDevice(net0); Names::Add("ESsource#01", net0); Ptr net1 = CreateObject(); n1->AddDevice(net1); Names::Add("ESdest#01", net1); Ptr net2_1 = CreateObject(); n2->AddDevice(net2_1); Ptr net2_2 = CreateObject(); n2->AddDevice(net2_2); Ptr net2_3 = CreateObject(); n2->AddDevice(net2_3); Ptr net3_1 = CreateObject(); n3->AddDevice(net3_1); Ptr net3_2 = CreateObject(); n3->AddDevice(net3_2); Ptr net4_1 = CreateObject(); n4->AddDevice(net4_1); Ptr net4_2 = CreateObject(); n4->AddDevice(net4_2); Ptr net5_1 = CreateObject(); n5->AddDevice(net5_1); Ptr net5_2 = CreateObject(); n5->AddDevice(net5_2); Ptr net5_3 = CreateObject(); n5->AddDevice(net5_3); //Create Ethernet Channels and attach it to the netDevices Ptr l0 = CreateObject(); l0->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net0->Attach(l0); net2_1->Attach(l0); Ptr l1 = CreateObject(); l1->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net2_2->Attach(l1); net3_1->Attach(l1); Ptr l2 = CreateObject(); l2->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net2_3->Attach(l2); net4_1->Attach(l2); Ptr l3 = CreateObject(); l3->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net3_2->Attach(l3); net5_1->Attach(l3); Ptr l4 = CreateObject(); l4->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net4_2->Attach(l4); net5_2->Attach(l4); Ptr l5 = CreateObject(); l5->SetAttribute("Delay", TimeValue(MicroSeconds(0))); net5_3->Attach(l5); net1->Attach(l5); //Create and add switche net devices to the switch nodes Ptr sw1 = CreateObject(); sw1->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10))); sw1->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10))); n2->AddDevice(sw1); sw1->AddSwitchPort(net2_1); sw1->AddSwitchPort(net2_2); sw1->AddSwitchPort(net2_3); Ptr sw2 = CreateObject(); sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10))); sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10))); n3->AddDevice(sw2); sw2->AddSwitchPort(net3_1); sw2->AddSwitchPort(net3_2); Ptr sw3 = CreateObject(); sw3->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10))); sw3->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10))); n4->AddDevice(sw3); sw3->AddSwitchPort(net4_1); sw3->AddSwitchPort(net4_2); Ptr sw4 = CreateObject(); sw4->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10))); sw4->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10))); n5->AddDevice(sw4); sw4->AddSwitchPort(net5_1); sw4->AddSwitchPort(net5_2); sw4->AddSwitchPort(net5_3); //Allocate a Mac address net0->SetAddress(Mac48Address::Allocate()); net1->SetAddress(Mac48Address::Allocate()); sw1->SetAddress(Mac48Address::Allocate()); sw2->SetAddress(Mac48Address::Allocate()); sw3->SetAddress(Mac48Address::Allocate()); sw4->SetAddress(Mac48Address::Allocate()); //Create and add eight FIFO on each net device for (int i=0; i<8; i++) { net0->SetQueue(CreateObject>()); net1->SetQueue(CreateObject>()); net2_1->SetQueue(CreateObject>()); net2_2->SetQueue(CreateObject>()); net2_3->SetQueue(CreateObject>()); net3_1->SetQueue(CreateObject>()); net3_2->SetQueue(CreateObject>()); net4_1->SetQueue(CreateObject>()); net4_2->SetQueue(CreateObject>()); net5_1->SetQueue(CreateObject>()); net5_2->SetQueue(CreateObject>()); net5_3->SetQueue(CreateObject>()); } //Add forwarding table sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {net2_2, net2_3}); sw2->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {net3_2}); sw3->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {net4_2}); sw4->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {net5_3}); sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {net2_2}); sw2->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {net3_2}); sw4->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {net5_3}); //Stream Indentification + FRER //First switch //Stream identification Ptr sif0 = CreateObject(); uint16_t StreamHandle = 1; sif0->SetAttribute("VlanID", UintegerValue(100)); sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff"))); n2->AddStreamIdentificationFunction(StreamHandle, sif0, {net2_1}, {}, {}, {}); //Sequencing : Sequence generation Ptr seqf0 = CreateObject(); seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing seqf0->SetStreamHandle({StreamHandle}); n2->AddSequenceGenerationFunction(seqf0); //Sequence encode Ptr seqEnc0 = CreateObject(); seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing seqEnc0->SetAttribute("Active", BooleanValue(true)); seqEnc0->SetStreamHandle({StreamHandle}); seqEnc0->SetPort(net2_1); n2->AddSequenceEncodeDecodeFunction(seqEnc0); //Last switch //Stream identification Ptr sif1 = CreateObject(); StreamHandle = 1; sif1->SetAttribute("VlanID", UintegerValue(100)); sif1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff"))); n5->AddStreamIdentificationFunction(StreamHandle, sif1, {}, {}, {net5_3}, {}); //Sequence Decode Ptr seqEnc1 = CreateObject(); seqEnc1->SetAttribute("Direction", BooleanValue(false)); //in-facing seqEnc1->SetAttribute("Active", BooleanValue(false)); seqEnc1->SetStreamHandle({StreamHandle}); seqEnc1->SetPort(net5_3); n5->AddSequenceEncodeDecodeFunction(seqEnc1); //Sequencing : Sequence recovery Ptr seqfreco0 = CreateObject(); seqfreco0->SetAttribute("Direction", BooleanValue(false)); //in-facing seqfreco0->SetAttribute("TakeNoSequence", BooleanValue(false)); seqfreco0->SetAttribute("IndividualRecovery", BooleanValue(false)); seqfreco0->SetStreamHandle({StreamHandle}); seqfreco0->SetPorts({net5_3}); n5->AddSequenceRecoveryFunction(seqfreco0); //Sequencing : Sequence recovery : recovery function Ptr recf0 = CreateObject(); seqfreco0->SetRecoveryFunction(recf0); //Sequencing : Sequence recovery : latent error detection function Ptr latf0 = CreateObject(); latf0->SetAttribute("LatentErrorPaths", UintegerValue(2)); latf0->SetRecoveryFunction(recf0); seqfreco0->SetLatentErrorDetectionFunction(latf0); //Application description Ptr app0 = CreateObject(); app0->Setup(net0); app0->SetAttribute("BurstSize", UintegerValue(1)); app0->SetAttribute("PayloadSize", UintegerValue(1400)); app0->SetAttribute("Period", TimeValue(MilliSeconds(20))); app0->SetAttribute("PCP", UintegerValue(1)); app0->SetAttribute("VlanID", UintegerValue(100)); n0->AddApplication(app0); app0->SetStartTime(MilliSeconds(0)); app0->SetStopTime(MilliSeconds(20)); Ptr app1 = CreateObject(); app1->Setup(net0); app1->SetAttribute("BurstSize", UintegerValue(1)); app1->SetAttribute("PayloadSize", UintegerValue(500)); app1->SetAttribute("Period", TimeValue(MilliSeconds(20))); app1->SetAttribute("PCP", UintegerValue(0)); app1->SetAttribute("VlanID", UintegerValue(101)); n0->AddApplication(app1); app1->SetStartTime(Seconds(0)); app1->SetStopTime(MilliSeconds(20)); //Callback to trace the message being replicated and eliminated net2_2->TraceConnectWithoutContext("MacTx", MakeCallback(&FRERBasicMulticastTestCase::SendTx, this)); net2_3->TraceConnectWithoutContext("MacTx", MakeCallback(&FRERBasicMulticastTestCase::SendTx, this)); net1->TraceConnectWithoutContext("MacRx", MakeCallback(&FRERBasicMulticastTestCase::ReceiveRx, this)); //Execute the simulation Simulator::Stop(MilliSeconds(50)); Simulator::Run(); Simulator::Destroy(); NS_TEST_ASSERT_MSG_EQ(m_replicated, m_expected_replicated, "All Packets have been replicated"); NS_TEST_ASSERT_MSG_EQ(m_received, m_expected_receive, "All Packets have been eliminated"); } // 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 FRER-tests * TestSuite for module FRER */ class FRERTestSuite : public TestSuite { public: FRERTestSuite(); }; FRERTestSuite::FRERTestSuite() : TestSuite("frer", UNIT) { LogComponentEnable("FRERTestSuite", LOG_LEVEL_ALL); //Pkt in a switched network with multicast FRER AddTestCase(new FRERBasicMulticastTestCase(), TestCase::QUICK); } // Do not forget to allocate an instance of this TestSuite /** * \ingroup FRER-tests * Static variable for test initialization */ static FRERTestSuite m_FRERTestSuite;