Update README and add contrib dir

This commit is contained in:
2025-12-01 15:56:02 +01:00
parent 1b80de2153
commit cd9ba93d58
150 changed files with 25563 additions and 0 deletions

View File

@@ -31,6 +31,8 @@ cd <path to your ns-3 installation>
./test.py --suite ethernet --text test-logs
./test.py --example ethernet-point2point-withPropagationDelay
```
Note that with version 3.40 of ns-3, the “neighbor-cache” test fails without any connection or consequence to our library. However, the “mobility-trace,” “attributes,” “data-rate,” and “time” tests fail because of our library. Indeed, in order to simulate communications at over 1Gb/s, the “ethernet-test-suite.cc” test sets the time resolution to picoseconds instead of nanoseconds, thus causing side effects in other tests. Line 996 (i.e. “Time::SetResolution(Time::PS);”) can be commented out to resolve this issue.
To run an example (e.g. "ethernet-point2point.cc") :
```console

View File

@@ -0,0 +1,28 @@
check_include_file_cxx(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
add_definitions(-DHAVE_STDINT_H)
endif()
set(examples_as_tests_sources)
if(${ENABLE_EXAMPLES})
set(examples_as_tests_sources
#test/ethernet-examples-test-suite.cc
)
endif()
build_lib(
LIBNAME ethernet
SOURCE_FILES model/ethernet-channel.cc
model/ethernet-header2.cc
model/ethernet-net-device.cc
model/switch-net-device.cc
model/switch-channel.cc
HEADER_FILES model/ethernet-channel.h
model/ethernet-header2.h
model/ethernet-net-device.h
model/switch-net-device.h
model/switch-channel.h
LIBRARIES_TO_LINK ${libcore}
TEST_SOURCES test/ethernet-test-suite.cc
${examples_as_tests_sources}
)

View File

@@ -0,0 +1,22 @@
set(base_examples
ethernet-point2point
ethernet-point2point-withPropagationDelay
ethernet-point2point-withSmallFifo
ethernet-switched
ethernet-switched-withLatencyCallback
ethernet-switched-withPcap
ethernet-switched-withRxTxCallback
)
foreach(
example
${base_examples}
)
build_lib_example(
NAME ${example}
SOURCE_FILES ${example}.cc
LIBRARIES_TO_LINK ${libcore}
${libnetwork}
${libethernet}
${libtraffic-generator}
)
endforeach()

View File

@@ -0,0 +1,94 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of ethernet-net-device.cc ethernet-channel.cc on a network
* composed of two end-stations connected by a 10Gb/s full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::FS) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Mandatory for multi-giga
Time::SetResolution(Time::PS);
//Create two nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("10Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("10Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,93 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of ethernet-net-device.cc ethernet-channel.cc on a network
* composed of two end-stations connected by a 100Mb/s full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel with a 10µs propagation delay (~2km) and attach
//it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
channel->SetAttribute("Delay", TimeValue(MicroSeconds(10)));
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(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,108 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of ethernet-net-device.cc ethernet-channel.cc on a network
* composed of two end-stations connected by a 100Mb/s full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt drop due to FIFO offerflow
static void
MacTxDropCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " drop due to FIFO overflow !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Allocate a FIFO for each netDevice.
//5 packets size FIFO for net0
Ptr<DropTailQueue<Packet>> q = CreateObject<DropTailQueue<Packet>>();
q->SetAttribute("MaxSize", QueueSizeValue(QueueSize("5p")));
net0->SetQueue(q);
//Default size FIFO (i.e. 100 packets) for net1
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)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Callback to display the packet drop log
context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTxDrop", MakeBoundCallback(&MacTxDropCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,91 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of ethernet-net-device.cc ethernet-channel.cc on a network
* composed of two end-stations connected by a 100Mb/s full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,188 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/switch-net-device.h"
/**
* \file
*
* Example of the use of switch-net-device.cc switch-channel.cc
* ethernet-net-device.cc ethernet-channel.cc on a network composed of three
* end-stations connected with a 1Gb/s full duplex link
* ES1 ==== SW ==== ES2
* ==== ES3
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
//A callback to write in a file the pkt latency hop by hop
static void
LatencyHopByHopCallback(Ptr<OutputStreamWrapper> stream, std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
*stream->GetStream() << context << " : Packet #"<< p->GetUid() << " from "<< ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") : ";
TimestampTag tag;
TypeId tid = tag.GetInstanceTypeId();
ByteTagIterator i = p->GetByteTagIterator();
while (i.HasNext())
{
ByteTagIterator::Item item = i.Next();
if (tid == item.GetTypeId())
{
item.GetTag(tag);
*stream->GetStream() << tag.GetTimestamp().GetNanoSeconds() << ",";
}
}
*stream->GetStream() << "\n";
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
Ptr<Node> n2 = CreateObject<Node>();
Names::Add("ES3", n2);
Ptr<Node> n3 = CreateObject<Node>();
Names::Add("SW", n3);
//Create and add a netDevice to each end station node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
//Create and add a netDevice to each switch port
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet0);
Names::Add("SW#01", swnet0);
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet1);
Names::Add("SW#02", swnet1);
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet2);
Names::Add("SW#03", swnet2);
//Create Ethernet 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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->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::ConvertFrom(net2->GetAddress()), 1, {swnet2});
//Application description
//ES1 -> ES3 with priority 1
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet latency log
std::string context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
//Callback to write the packet latency hop by hop in a file
AsciiTraceHelper asciiTraceHelper;
std::string filename = "example.hopByHopLat";
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream(filename);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyHopByHopCallback, stream, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,176 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/trace-helper.h"
#include <fstream>
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/switch-net-device.h"
/**
* \file
*
* Example of the use of switch-net-device.cc switch-channel.cc
* ethernet-net-device.cc ethernet-channel.cc on a network composed of three
* end-stations connected with a 1Gb/s full duplex link
* ES1 ==== SW ==== ES2
* ==== ES3
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
Ptr<Node> n2 = CreateObject<Node>();
Names::Add("ES3", n2);
Ptr<Node> n3 = CreateObject<Node>();
Names::Add("SW", n3);
//Create and add a netDevice to each end station node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
//Create and add a netDevice to each switch port
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet0);
Names::Add("SW#01", swnet0);
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet1);
Names::Add("SW#02", swnet1);
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet2);
Names::Add("SW#03", swnet2);
//Create Ethernet 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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->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::ConvertFrom(net2->GetAddress()), 1, {swnet2});
//Application description
//ES1 -> ES3 with priority 1
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//ES2 -> ES1 and ES3 with priority 0
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net1);
app1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(2.5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("PCP", UintegerValue(0));
n1->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(5));
//Enable pcap generation
PcapHelper pcapHelper;
std::string pcapFilename;
std::string prefix = "example";
Ptr<PcapFileWrapper> file;
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net0);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net0, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net1);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net1, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net2);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net2, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, swnet0);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(swnet0, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, swnet1);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(swnet1, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, swnet2);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(swnet2, "Sniffer", file);
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,172 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/trace-helper.h"
#include <fstream>
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/switch-net-device.h"
/**
* \file
*
* Example of the use of switch-net-device.cc switch-channel.cc
* ethernet-net-device.cc ethernet-channel.cc on a network composed of three
* end-stations connected with a 1Gb/s full duplex link
* ES1 ==== SW ==== ES2
* ==== ES3
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
static void
MacTxCallback(Ptr<OutputStreamWrapper> stream, std::string context, Ptr<const Packet> p)
{
Time t = Simulator::Now();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
*stream->GetStream() << "NetDevice:" << context << " : At "<< t.GetNanoSeconds() << " FIFO Entry => Packet #"<< p->GetUid() << " from "<< ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ")\n";
}
static void
PhyTxBeginCallback(Ptr<OutputStreamWrapper> stream, std::string context, Ptr<const Packet> p)
{
Time t = Simulator::Now();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
*stream->GetStream() << "NetDevice:" << context << " : At "<< t.GetNanoSeconds() << " FIFO Exit => Packet #"<< p->GetUid() << " from "<< ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ")\n";
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
Ptr<Node> n2 = CreateObject<Node>();
Names::Add("ES3", n2);
Ptr<Node> n3 = CreateObject<Node>();
Names::Add("SW", n3);
//Create and add a netDevice to each end station node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
//Create and add a netDevice to each switch port
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet0);
Names::Add("SW#01", swnet0);
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet1);
Names::Add("SW#02", swnet1);
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet2);
Names::Add("SW#03", swnet2);
//Create Ethernet 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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->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::ConvertFrom(net2->GetAddress()), 1, {swnet2});
//Application description
//ES1 -> ES3 with priority 1
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to wirte FIFO entry and exit event in a file
AsciiTraceHelper asciiTraceHelper;
std::string filename = "example.fifo";
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream(filename);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(net0)));
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(net1)));
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(net2)));
net2->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(net2)));
swnet0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(swnet0)));
swnet0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(swnet0)));
swnet1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(swnet1)));
swnet1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(swnet1)));
swnet2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, stream, Names::FindName(swnet2)));
swnet2->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxBeginCallback, stream, Names::FindName(swnet2)));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,158 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/switch-net-device.h"
/**
* \file
*
* Example of the use of switch-net-device.cc switch-channel.cc
* ethernet-net-device.cc ethernet-channel.cc on a network composed of three
* end-stations connected with a 1Gb/s full duplex link
* ES1 ==== SW ==== ES2
* ==== ES3
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
Ptr<Node> n2 = CreateObject<Node>();
Names::Add("ES3", n2);
Ptr<Node> n3 = CreateObject<Node>();
Names::Add("SW", n3);
//Create and add a netDevice to each end station node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<EthernetNetDevice> net2 = CreateObject<EthernetNetDevice>();
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
//Create and add a netDevice to each switch port
Ptr<EthernetNetDevice> swnet0 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet0);
Names::Add("SW#01", swnet0);
Ptr<EthernetNetDevice> swnet1 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet1);
Names::Add("SW#02", swnet1);
Ptr<EthernetNetDevice> swnet2 = CreateObject<EthernetNetDevice>();
n3->AddDevice(swnet2);
Names::Add("SW#03", swnet2);
//Create Ethernet 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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->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::ConvertFrom(net2->GetAddress()), 1, {swnet2});
sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 2, {swnet0, swnet2});
//Application descriptions
//ES1 -> ES3 with priority 1
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(5));
//ES2 -> ES1 and ES3 with priority 0
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net1);
app1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(2.5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("PCP", UintegerValue(0));
n1->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(5));
//Callback to display the packet received log
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(5));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,137 @@
#include "ethernet-channel.h"
#include "ethernet-net-device.h"
#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/simulator.h"
#include "ns3/trace-source-accessor.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EthernetChannel");
NS_OBJECT_ENSURE_REGISTERED(EthernetChannel);
TypeId
EthernetChannel::GetTypeId()
{
static TypeId tid =
TypeId("ns3::EthernetChannel")
.SetParent<Channel>()
.SetGroupName("Ethernet")
.AddConstructor<EthernetChannel>()
.AddAttribute("Delay",
"Propagation delay through the channel",
TimeValue(NanoSeconds(25)),
MakeTimeAccessor(&EthernetChannel::m_delay),
MakeTimeChecker())
.AddTraceSource("TxRxEthernet",
"Trace source indicating transmission of packet "
"from the EthernetChannel, used by the Animation "
"interface.",
MakeTraceSourceAccessor(&EthernetChannel::m_txrxEthernet),
"ns3::EthernetChannel::TxRxAnimationCallback");
return tid;
}
EthernetChannel::EthernetChannel()
: Channel(),
m_nDevices(0)
{
NS_LOG_FUNCTION_NOARGS();
}
void
EthernetChannel::Attach(Ptr<EthernetNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
NS_ASSERT_MSG(m_nDevices < N_DEVICES, "Only two devices permitted");
NS_ASSERT(device);
m_wire[m_nDevices++].m_src = device;
//
// If we have both devices connected to the channel, then finish introducing
// the two halves and set the links to IDLE.
//
if (m_nDevices == N_DEVICES)
{
m_wire[0].m_dst = m_wire[1].m_src;
m_wire[1].m_dst = m_wire[0].m_src;
m_wire[0].m_state = IDLE;
m_wire[1].m_state = IDLE;
}
}
bool
EthernetChannel::TransmitStart(Ptr<const Packet> p, Ptr<EthernetNetDevice> src, Time txTime)
{
NS_LOG_FUNCTION(this << p << src);
NS_LOG_LOGIC("UID is " << p->GetUid() << ")");
NS_ASSERT(m_wire[0].m_state != INITIALIZING);
NS_ASSERT(m_wire[1].m_state != INITIALIZING);
uint32_t wire = src == m_wire[0].m_src ? 0 : 1;
Simulator::ScheduleWithContext(m_wire[wire].m_dst->GetNode()->GetId(),
txTime + m_delay,
&EthernetNetDevice::Receive,
m_wire[wire].m_dst,
p->Copy());
// Call the tx anim callback on the net device
m_txrxEthernet(p, src, m_wire[wire].m_dst, txTime, txTime + m_delay);
return true;
}
std::size_t
EthernetChannel::GetNDevices() const
{
NS_LOG_FUNCTION_NOARGS();
return m_nDevices;
}
Ptr<EthernetNetDevice>
EthernetChannel::GetEthernetDevice(std::size_t i) const
{
NS_LOG_FUNCTION_NOARGS();
NS_ASSERT(i < 2);
return m_wire[i].m_src;
}
Ptr<NetDevice>
EthernetChannel::GetDevice(std::size_t i) const
{
NS_LOG_FUNCTION_NOARGS();
return GetEthernetDevice(i);
}
Time
EthernetChannel::GetDelay() const
{
return m_delay;
}
Ptr<EthernetNetDevice>
EthernetChannel::GetSource(uint32_t i) const
{
return m_wire[i].m_src;
}
Ptr<EthernetNetDevice>
EthernetChannel::GetDestination(uint32_t i) const
{
return m_wire[i].m_dst;
}
bool
EthernetChannel::IsInitialized() const
{
NS_ASSERT(m_wire[0].m_state != INITIALIZING);
NS_ASSERT(m_wire[1].m_state != INITIALIZING);
return true;
}
}

View File

@@ -0,0 +1,207 @@
#ifndef ETHERNET_CHANNEL_H
#define ETHERNET_CHANNEL_H
#include "ns3/channel.h"
#include "ns3/data-rate.h"
#include "ns3/nstime.h"
#include "ns3/ptr.h"
#include "ns3/traced-callback.h"
#include <list>
// Add a doxygen group for this module.
// If you have more than one file, this should be in only one of them.
/**
* \defgroup ethernet Description of the ethernet
*/
namespace ns3
{
class EthernetNetDevice;
class Packet;
/**
* \ingroup ethernet
* \brief Simple Ethernet Channel.
*
* This class represents a very simple full duplex Ethernet channel. It is
* largely inspired by the PointToPointChannel. There is no multi-drop
* capability on this channel -- there can be a maximum of two Ethernet
* net devices connected.
*
* There are two "wires" in the channel. The first device connected gets the
* [0] wire to transmit on. The second device gets the [1] wire. There is a
* state (IDLE, TRANSMITTING) associated with each wire.
*
* \see Attach
* \see TransmitStart
*/
class EthernetChannel : public Channel
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a EthernetChannel
*
* By default, you get a channel that has a 25ns delay.
*/
EthernetChannel();
/**
* \brief Attach a given netdevice to this channel
* \param device pointer to the netdevice to attach to the channel
*/
void Attach(Ptr<EthernetNetDevice> device);
/**
* \brief Transmit a packet over this channel
* \param p Packet to transmit
* \param src Source EthernetNetDevice
* \param txTime Transmit time to apply
* \returns true if successful (currently always true)
*/
virtual bool TransmitStart(Ptr<const Packet> p, Ptr<EthernetNetDevice> src, Time txTime);
/**
* \brief Get number of devices on this channel
* \returns number of devices on this channel
*/
std::size_t GetNDevices() const override;
/**
* \brief Get EthernetNetDevice corresponding to index i on this channel
* \param i Index number of the device requested
* \returns Ptr to EthernetNetDevice requested
*/
Ptr<EthernetNetDevice> GetEthernetDevice(std::size_t i) const;
/**
* \brief Get NetDevice corresponding to index i on this channel
* \param i Index number of the device requested
* \returns Ptr to NetDevice requested
*/
Ptr<NetDevice> GetDevice(std::size_t i) const override;
protected:
/**
* \brief Get the delay associated with this channel
* \returns Time delay
*/
Time GetDelay() const;
/**
* \brief Check to make sure the wire is initialized
* \returns true if initialized, asserts otherwise
*/
bool IsInitialized() const;
/**
* \brief Get the net-device source
* \param i the wire requested
* \returns Ptr to EthernetNetDevice source for the
* specified wire
*/
Ptr<EthernetNetDevice> GetSource(uint32_t i) const;
/**
* \brief Get the net-device destination
* \param i the wire requested
* \returns Ptr to EthernetNetDevice destination for
* the specified wire
*/
Ptr<EthernetNetDevice> GetDestination(uint32_t i) const;
/**
* TracedCallback signature for packet transmission animation events.
*
* \param [in] packet The packet being transmitted.
* \param [in] txDevice the TransmitTing NetDevice.
* \param [in] rxDevice the Receiving NetDevice.
* \param [in] duration The amount of time to transmit the packet.
* \param [in] lastBitTime Last bit receive time (relative to now)
* \deprecated The non-const \c Ptr<NetDevice> argument is deprecated
* and will be changed to \c Ptr<const NetDevice> in a future release.
*/
typedef void (*TxRxAnimationCallback)(Ptr<const Packet> packet,
Ptr<NetDevice> txDevice,
Ptr<NetDevice> rxDevice,
Time duration,
Time lastBitTime);
private:
/** Each ethernet full duplex wire has exactly two net devices. */
static const std::size_t N_DEVICES = 2;
Time m_delay; //!< Propagation delay
std::size_t m_nDevices; //!< Devices of this channel
/**
* The trace source for the packet transmission animation events that the
* device can fire.
* Arguments to the callback are the packet, transmitting
* net device, receiving net device, transmission time and
* packet receipt time.
*
* \see class CallBackTraceSource
* \deprecated The non-const \c Ptr<NetDevice> argument is deprecated
* and will be changed to \c Ptr<const NetDevice> in a future release.
*/
TracedCallback<Ptr<const Packet>, // Packet being transmitted
Ptr<NetDevice>, // Transmitting NetDevice
Ptr<NetDevice>, // Receiving NetDevice
Time, // Amount of time to transmit the pkt
Time // Last bit receive time (relative to now)
>
m_txrxEthernet;
/** \brief Wire states
*
*/
enum WireState
{
/** Initializing state */
INITIALIZING,
/** Idle state (no transmission from NetDevice) */
IDLE,
/** Transmitting state (data being transmitted from NetDevice. */
TRANSMITTING,
/** Propagating state (data is being propagated in the channel. */
PROPAGATING
};
/**
* \brief Wire model for the EthernetChannel
*/
class Wire
{
public:
/** \brief Create the wire, it will be in INITIALIZING state
*
*/
Wire()
: m_state(INITIALIZING),
m_src(nullptr),
m_dst(nullptr)
{
}
WireState m_state; //!< State of the wire
Ptr<EthernetNetDevice> m_src; //!< First NetDevice
Ptr<EthernetNetDevice> m_dst; //!< Second NetDevice
};
Wire m_wire[N_DEVICES]; //!< Wire model
};
}
#endif /* ETHERNET_CHANNEL_H */

View File

@@ -0,0 +1,277 @@
#include "ethernet-header2.h"
#include "ns3/abort.h"
#include "ns3/assert.h"
#include "ns3/header.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include <iomanip>
#include <iostream>
#include "ns3/address-utils.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EthernetHeader2");
NS_OBJECT_ENSURE_REGISTERED(EthernetHeader2);
EthernetHeader2::EthernetHeader2()
{
NS_LOG_FUNCTION_NOARGS();
}
EthernetHeader2::~EthernetHeader2()
{
NS_LOG_FUNCTION_NOARGS();
}
TypeId
EthernetHeader2::GetTypeId()
{
NS_LOG_FUNCTION_NOARGS();
static TypeId tid = TypeId("ns3::EthernetHeader2")
.SetParent<Header>()
.SetGroupName("Ethernet")
.AddConstructor<EthernetHeader2>();
return tid;
}
TypeId
EthernetHeader2::GetInstanceTypeId() const
{
NS_LOG_FUNCTION_NOARGS();
return GetTypeId();
}
void
EthernetHeader2::Print(std::ostream& os) const
{
NS_LOG_FUNCTION(this);
std::string proto;
os << m_src << "->" << m_dest << " with ";
for(int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type == VLAN)
{
os <<"vlan_id=" << (m_QTagList[i].TCI & 0xFFF) << " ";
}
if(m_QTagList[i].Type == REDUNDANCY)
{
os <<"RTAG=True ";
}
}
}
uint32_t
EthernetHeader2::GetSerializedSize() const
{
NS_LOG_FUNCTION_NOARGS();
int tagSize = 0;
for (int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type==VLAN)
{
tagSize += 4;
}
else if(m_QTagList[i].Type==REDUNDANCY)
{
tagSize += 6;
}
}
return 6 + 6 + tagSize + 2;
}
void
EthernetHeader2::Serialize(Buffer::Iterator start) const
{
NS_LOG_FUNCTION(this);
Buffer::Iterator i = start;
WriteTo(i, m_dest);
WriteTo(i, m_src);
for (int j=0; j<(int)m_QTagList.size() ; j++)
{
i.WriteHtonU16(m_QTagList[j].TPID);
if(m_QTagList[j].Type == VLAN)
{
i.WriteHtonU16(m_QTagList[j].TCI);
}
else if(m_QTagList[j].Type == REDUNDANCY)
{
i.WriteHtonU32(m_QTagList[j].TCI);
}
}
i.WriteHtonU16(m_ethertype);
}
uint32_t
EthernetHeader2::Deserialize(Buffer::Iterator start)
{
NS_LOG_FUNCTION(this);
Buffer::Iterator i = start;
ReadFrom(i, m_dest);
ReadFrom(i, m_src);
uint16_t tmp = i.ReadNtohU16();
while (tmp==0x8100 or tmp==0xf1c1){
TagType type = VLAN;
uint32_t tci = 0;
if (tmp==0x8100){
type = VLAN;
tci = i.ReadNtohU16();
}
else if (tmp==0xf1c1){
type = REDUNDANCY;
tci = i.ReadNtohU32();
}
Qtag entry = {tmp, tci, type};
m_QTagList.insert(m_QTagList.end(), entry);
tmp = i.ReadNtohU16();
}
m_ethertype = tmp;
// NS_LOG_INFO("m_QTagList size = " << m_QTagList.size());
return GetSerializedSize();
}
void
EthernetHeader2::SetDest(Mac48Address addr)
{
NS_LOG_FUNCTION(this);
m_dest = addr;
}
Mac48Address
EthernetHeader2::GetDest()
{
NS_LOG_FUNCTION(this);
return m_dest;
}
void
EthernetHeader2::SetSrc(Mac48Address addr)
{
NS_LOG_FUNCTION(this);
m_src = addr;
}
Mac48Address
EthernetHeader2::GetSrc()
{
NS_LOG_FUNCTION(this);
return m_src;
}
uint8_t
EthernetHeader2::GetPcp()
{
NS_LOG_FUNCTION(this);
for(int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type == VLAN)
{
return (m_QTagList[i].TCI >> 13) & 0x7;
}
}
NS_ASSERT_MSG(true, "Can't find a Vlan QTag in this header");
return 0;
}
uint8_t
EthernetHeader2::GetDei()
{
NS_LOG_FUNCTION(this);
for(int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type == VLAN)
{
return uint8_t((m_QTagList[i].TCI >> 12) & 0x1);
}
}
NS_ASSERT_MSG(true, "Can't find a Vlan QTag in this header");
return 0;
}
uint16_t
EthernetHeader2::GetVid()
{
NS_LOG_FUNCTION(this);
for(int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type == VLAN)
{
return (m_QTagList[i].TCI & 0xFFF);
}
}
NS_ASSERT_MSG(true, "Can't find a Vlan QTag in this header");
return 0;
}
void
EthernetHeader2::SetEthertype(uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
m_ethertype = ethertype;
}
uint16_t
EthernetHeader2::GetEthertype()
{
NS_LOG_FUNCTION(this);
return m_ethertype;
}
void
EthernetHeader2::SetVlanTag(uint8_t pcp, uint8_t dei, uint16_t vid)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(vid < 4096 , "Vlan id must be between 0 and 4095");
NS_ASSERT_MSG(dei < 2 , "DEI must be between 0 and 1");
NS_ASSERT_MSG(pcp < 8 , "PCP must be between 0 and 7");
uint16_t tpid = 0x8100;
uint32_t tci = (pcp << 13) | (dei << 12) | vid;
Qtag entry = {tpid, tci, VLAN};
m_QTagList.insert(m_QTagList.begin(), entry);
}
void
EthernetHeader2::SetRedundancyTag(uint16_t seqNum)
{
NS_LOG_FUNCTION(this);
uint16_t tpid = 0xf1c1;
uint32_t tci = (0 << 16) | seqNum;
Qtag entry = {tpid, tci, REDUNDANCY};
m_QTagList.insert(m_QTagList.begin(), entry);
}
uint16_t
EthernetHeader2::RemoveRedundancyTag()
{
NS_LOG_FUNCTION(this);
for (int i=0; i<(int)m_QTagList.size(); i++)
{
if(m_QTagList[i].Type == REDUNDANCY)
{
uint16_t seqNum = m_QTagList[i].TCI & 0xFFFF;
m_QTagList.erase(m_QTagList.begin() + i);
return seqNum;
}
}
NS_ASSERT_MSG(true, "Can't find a Redundancy QTag in this header");
return 0;
}
}

View File

@@ -0,0 +1,86 @@
#ifndef ETHERNET_HEADER2_H
#define ETHERNET_HEADER2_H
#include "ns3/header.h"
#include "ns3/mac48-address.h"
namespace ns3
{
/**
* \ingroup ethernet
* \brief Packet header for Ethernet
*
* This class can be used to add a header to Ethernet packet.
*/
class EthernetHeader2 : public Header
{
public:
/**
* \brief Construct a Ethernet header.
*/
EthernetHeader2();
/**
* \brief Destroy a Ethernet header.
*/
~EthernetHeader2() override;
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Get the TypeId of the instance
*
* \return The TypeId for this instance
*/
TypeId GetInstanceTypeId() const override;
void Print(std::ostream& os) const override;
void Serialize(Buffer::Iterator start) const override;
uint32_t Deserialize(Buffer::Iterator start) override;
uint32_t GetSerializedSize() const override;
void SetDest(Mac48Address addr);
Mac48Address GetDest();
void SetSrc(Mac48Address addr);
Mac48Address GetSrc();
uint8_t GetPcp();
uint8_t GetDei();
uint16_t GetVid();
void SetEthertype(uint16_t ethertype);
uint16_t GetEthertype();
void SetVlanTag(uint8_t pcp, uint8_t dei, uint16_t vid);
void SetRedundancyTag(uint16_t seqNum);
uint16_t RemoveRedundancyTag();
private:
Mac48Address m_dest;
Mac48Address m_src;
//QTag (VlanTag, RedundancyTag ...)
enum TagType
{
VLAN,
REDUNDANCY,
};
struct Qtag
{
uint16_t TPID; //Tag Protocol Identifier
uint32_t TCI; //Tag Control Information
TagType Type;
};
std::vector<Qtag> m_QTagList;
uint16_t m_ethertype;
};
} // namespace eden
#endif /* ETHERNET_HEADER2_H */

View File

@@ -0,0 +1,769 @@
#include "ethernet-net-device.h"
#include "ethernet-channel.h"
#include "ethernet-header2.h"
#include "ns3/error-model.h"
#include "ns3/llc-snap-header.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/pointer.h"
#include "ns3/queue.h"
#include "ns3/simulator.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/uinteger.h"
#include "ns3/ethernet-trailer.h"
#include "ns3/timestamp-tag.h"
#include "ns3/names.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EthernetNetDevice");
NS_OBJECT_ENSURE_REGISTERED(EthernetNetDevice);
TypeId
EthernetNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::EthernetNetDevice")
.SetParent<NetDevice>()
.SetGroupName("Ethernet")
.AddConstructor<EthernetNetDevice>()
.AddAttribute("Mtu",
"The MAC-level Maximum Transmission Unit",
UintegerValue(DEFAULT_MTU),
MakeUintegerAccessor(&EthernetNetDevice::SetMtu,
&EthernetNetDevice::GetMtu),
MakeUintegerChecker<uint16_t>())
.AddAttribute("Address",
"The MAC address of this device.",
Mac48AddressValue(Mac48Address("00:00:00:00:ED:E1")),
MakeMac48AddressAccessor(&EthernetNetDevice::m_address),
MakeMac48AddressChecker())
.AddAttribute("DataRate",
"The default data rate for point to point links",
DataRateValue(DataRate("1Gb/s")),
MakeDataRateAccessor(&EthernetNetDevice::m_bps),
MakeDataRateChecker())
.AddAttribute("ReceiveErrorModel",
"The receiver error model used to simulate packet loss",
PointerValue(),
MakePointerAccessor(&EthernetNetDevice::m_receiveErrorModel),
MakePointerChecker<ErrorModel>())
.AddAttribute("preambleAndSFDGap",
"Number of byte for the preamble and start of frame delimiter gap",
UintegerValue(8),
MakeUintegerAccessor(&EthernetNetDevice::m_preambleAndSFDGap),
MakeUintegerChecker<uint8_t>())
.AddAttribute("InterframeGap",
"Number of byte for the interframe gap",
UintegerValue(12),
MakeUintegerAccessor(&EthernetNetDevice::m_interframeGap),
MakeUintegerChecker<uint8_t>())
//
// Trace sources at the "top" of the net device, where packets transition
// to/from higher layers.
//
.AddTraceSource("MacTx",
"Trace source indicating a packet has arrived "
"for transmission by this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacTxAnimation",
"Trace source indicating a packet has arrived "
"for transmission by this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxAnimationTrace),
"ns3::EthernetNetDevice::MacTxAnimationCallback")
.AddTraceSource("MacTxDrop",
"Trace source indicating a packet has been dropped "
"by the device before transmission",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacPromiscRx",
"A packet has been received by this device, "
"has been passed up from the physical layer "
"and is being forwarded up the local protocol stack. "
"This is a promiscuous trace,",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macPromiscRxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacRx",
"A packet has been received by this device, "
"has been passed up from the physical layer "
"and is being forwarded up the local protocol stack. "
"This is a non-promiscuous trace,",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macRxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacRxAnimation",
"Trace source indicating a packet has arrived "
"in this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macRxAnimationTrace),
"ns3::EthernetNetDevice::MacRxAnimationCallback")
#if 0
// Not currently implemented for this device
.AddTraceSource ("MacRxDrop",
"Trace source indicating a packet was dropped "
"before being forwarded up the stack",
MakeTraceSourceAccessor (&EthernetNetDevice::m_macRxDropTrace),
"ns3::Packet::TracedCallback")
#endif
//
// Trace sources at the "bottom" of the net device, where packets transition
// to/from the channel.
//
.AddTraceSource("PhyTxBegin",
"Trace source indicating a packet has begun "
"transmitting over the channel",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxBeginTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyTxEnd",
"Trace source indicating a packet has been "
"completely transmitted over the channel",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxEndTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyTxDrop",
"Trace source indicating a packet has been "
"dropped by the device during transmission",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("Latency",
"Trace source to compute network latency ",
MakeTraceSourceAccessor(&EthernetNetDevice::m_latencyTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("FIFOState",
"Trace source to study the FIFO state ",
MakeTraceSourceAccessor(&EthernetNetDevice::m_FIFOStateSnifferTrace),
"ns3::EthernetNetDevice::m_FIFOStateSnifferTrace")
#if 0
// Not currently implemented for this device
.AddTraceSource ("PhyRxBegin",
"Trace source indicating a packet has begun "
"being received by the device",
MakeTraceSourceAccessor (&EthernetNetDevice::m_phyRxBeginTrace),
"ns3::Packet::TracedCallback")
#endif
.AddTraceSource("PhyRxEnd",
"Trace source indicating a packet has been "
"completely received by the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyRxEndTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyRxDrop",
"Trace source indicating a packet has been "
"dropped by the device during reception",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyRxDropTrace),
"ns3::Packet::TracedCallback")
//
// Trace sources designed to simulate a packet sniffer facility (tcpdump).
// Note that there is really no difference between promiscuous and
// non-promiscuous traces in a point-to-point link.
//
.AddTraceSource("Sniffer",
"Trace source simulating a non-promiscuous packet sniffer "
"attached to the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_snifferTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PromiscSniffer",
"Trace source simulating a promiscuous packet sniffer "
"attached to the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_promiscSnifferTrace),
"ns3::Packet::TracedCallback");
return tid;
}
EthernetNetDevice::EthernetNetDevice()
{
NS_LOG_FUNCTION(this);
m_txMachineState = READY;
m_channel = nullptr;
m_linkUp = false;
m_currentPkt = nullptr;
}
EthernetNetDevice::~EthernetNetDevice()
{
NS_LOG_FUNCTION(this);
}
void
EthernetNetDevice::AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
EthernetHeader2 ethHeader;
ethHeader.SetDest(dst);
ethHeader.SetSrc(src);
ethHeader.SetEthertype(ethertype);
p->AddHeader(ethHeader);
}
void
EthernetNetDevice::AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
NS_LOG_FUNCTION(this);
EthernetHeader2 ethHeader;
ethHeader.SetDest(dst);
ethHeader.SetSrc(src);
ethHeader.SetEthertype(ethertype);
ethHeader.SetVlanTag(pcp, dei, vid);
p->AddHeader(ethHeader);
}
void
EthernetNetDevice::DoDispose()
{
NS_LOG_FUNCTION(this);
m_node = nullptr;
m_channel = nullptr;
m_receiveErrorModel = nullptr;
m_currentPkt = nullptr;
m_queues.clear();
NetDevice::DoDispose();
}
void
EthernetNetDevice::SetDataRate(DataRate bps)
{
NS_LOG_FUNCTION(this);
m_bps = bps;
}
bool
EthernetNetDevice::TransmitStart(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this << p);
NS_ASSERT_MSG(m_channel != nullptr, "Channel ptr is null");
NS_LOG_INFO(Names::FindName(this) << " PKT #" << p->GetUid() << " at " << Simulator::Now().GetNanoSeconds()<<" Transmit started ");
//Add a tag to compute delay between reception(or FIFO enter if first
//emission port) and transmission start
TimestampTag tag(Simulator::Now());
p->AddByteTag(tag);
//
// This function is called to start the process of transmitting a packet.
// We need to tell the channel that we've started wiggling the wire and
// schedule an event that will be executed when the transmission is complete.
//
NS_ASSERT_MSG(m_txMachineState == READY, "Must be READY to transmit");
m_txMachineState = BUSY;
m_currentPkt = p;
m_phyTxBeginTrace(m_currentPkt);
Time preambleAndSFDGapTime = m_bps.CalculateBytesTxTime(m_preambleAndSFDGap);
Time txTime = m_bps.CalculateBytesTxTime(p->GetSize());
Time interpacketGapTime = m_bps.CalculateBytesTxTime(m_interframeGap);
Time txCompleteTime = preambleAndSFDGapTime + txTime + interpacketGapTime;
//NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Transmit pkt #"<<p->GetUid());
//NS_LOG_LOGIC(Names::FindName(this) << " Schedule TransmitCompleteEvent in " << txCompleteTime.As(Time::S) << " for PKT #" << p->GetUid());
Simulator::Schedule(txCompleteTime, &EthernetNetDevice::TransmitComplete, this);
bool result = m_channel->TransmitStart(p, this, txCompleteTime-interpacketGapTime); //Pkt don't need to wait for the end of Interpacket gap to be considered as receive on the other end of the link
if (!result)
{
m_phyTxDropTrace(p);
}
return result;
}
void
EthernetNetDevice::TransmitComplete()
{
NS_LOG_FUNCTION(this);
NS_LOG_INFO(Names::FindName(this) << " PKT #" << m_currentPkt->GetUid() << " at " << Simulator::Now().GetNanoSeconds()<<" Transmit completed ");
// NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Transmit completed pkt ");
//
// This function is called to when we're all done transmitting a packet.
// We try and pull another packet off of the transmit queue. If the queue
// is empty, we are done, otherwise we need to start transmitting the
// next packet.
//
NS_ASSERT_MSG(m_txMachineState == BUSY, "Must be BUSY if transmitting");
m_txMachineState = READY;
NS_ASSERT_MSG(m_currentPkt, "EthernetNetDevice::TransmitComplete(): m_currentPkt zero");
m_phyTxEndTrace(m_currentPkt);
m_currentPkt = nullptr;
//Add the event after all the already present at this exact instant to avoid
//the beginning of a transmission if a packet will be ready at the event just
//after but at the same time instant.
Simulator::Schedule(Time(0), &EthernetNetDevice::CheckForReadyPacket, this);
}
void
EthernetNetDevice::CheckForReadyPacket()
{
NS_LOG_FUNCTION(this);
if (m_txMachineState != READY){
return;
}
int next_queue_id = TransmitSelection();
if(next_queue_id==-1)
{
return;
}
Ptr<Packet> p = m_queues[next_queue_id]->Dequeue();
//
// Got another packet off of the queue, so start the transmit process again.
//
m_snifferTrace(p);
m_promiscSnifferTrace(p);
TransmitStart(p);
}
int
EthernetNetDevice::TransmitSelection()
{
NS_LOG_FUNCTION(this);
//For now simple Static Priority Algo
for (int i = m_queues.size(); i --> 0; )
{
if(!m_queues[i]->IsEmpty())
{
return(i);
}
}
return(-1);
}
bool
EthernetNetDevice::Attach(Ptr<EthernetChannel> ch)
{
NS_LOG_FUNCTION(this << &ch);
m_channel = ch;
m_channel->Attach(this);
//
// This device is up whenever it is attached to a channel. A better plan
// would be to have the link come up when both devices are attached, but this
// is not done for now.
//
NotifyLinkUp();
return true;
}
void
EthernetNetDevice::SetQueue(Ptr<Queue<Packet>> q)
{
NS_LOG_FUNCTION(this << q);
m_queues.insert(m_queues.end(),q);
}
void
EthernetNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em)
{
NS_LOG_FUNCTION(this << em);
m_receiveErrorModel = em;
}
void
EthernetNetDevice::Receive(Ptr<Packet> packet)
{
NS_LOG_FUNCTION(this << packet);
NS_LOG_LOGIC("UID is " << packet->GetUid());
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt(packet))
{
//
// If we have an error model and it indicates that it is time to lose a
// corrupted packet, don't forward this packet up, let it go.
//
NS_LOG_LOGIC("Dropping pkt due to error model ");
m_phyRxDropTrace(packet);
}
else
{
//Add timestamp to measure delay in the switches
TimestampTag tag(Simulator::Now());
packet->AddByteTag(tag);
//
// Hit the trace hooks. All of these hooks are in the same place in this
// device because it is so simple, but this is not usually the case in
// more complicated devices.
//
m_snifferTrace(packet);
m_promiscSnifferTrace(packet);
m_phyRxEndTrace(packet);
//
// Trace sinks will expect complete packets, not packets without some of the
// headers.
//
Ptr<Packet> originalPacket = packet->Copy();
EthernetTrailer trailer;
packet->RemoveTrailer(trailer);
if (Node::ChecksumEnabled())
{
trailer.EnableFcs(true);
}
bool crcGood = trailer.CheckFcs(packet);
if (!crcGood)
{
NS_LOG_INFO("CRC error on Packet " << packet);
m_phyRxDropTrace(packet);
return;
}
//
// Strip off the ethernet protocol header and forward this packet
// up the protocol stack. Since this is a simple point-to-point link,
// there is no difference in what the promisc callback sees and what the
// normal receive callback sees.
//
EthernetHeader2 header;
packet->RemoveHeader(header);
uint16_t protocol;
protocol = header.GetEthertype();
//
// Classify the packet based on its destination.
//
PacketType packetType;
if (header.GetDest() == m_address)
{
packetType = PACKET_HOST;
}
else if (header.GetDest().IsGroup())
{
packetType = PACKET_MULTICAST;
}
else if (header.GetDest().IsBroadcast())
{
packetType = PACKET_BROADCAST;
}
else
{
packetType = PACKET_OTHERHOST;
}
if (!m_promiscCallback.IsNull())
{
m_macPromiscRxTrace(originalPacket->Copy());
m_promiscCallback(this,
originalPacket,
protocol,
header.GetSrc(),
header.GetDest(),
packetType);
}
//
// If this packet is not destined for some other host, it must be for us
// as either a broadcast, multicast or unicast. We need to hit the mac
// packet received trace hook and forward the packet up the stack.
//
if (packetType != PACKET_OTHERHOST)
{
//NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << Names::FindName(m_node) << "/" << Names::FindName(this) <<" : Pkt #" << originalPacket->GetUid() <<" received by the netDevice ! " << originalPacket->ToString());
m_macRxTrace(originalPacket);
m_macRxAnimationTrace(originalPacket, this);
m_rxCallback(this, packet, protocol, header.GetSrc());
m_latencyTrace(originalPacket);
}
}
}
Ptr<Queue<Packet>>
EthernetNetDevice::GetQueue(uint8_t index) const
{
NS_LOG_FUNCTION(this);
return m_queues[index];
}
void
EthernetNetDevice::NotifyLinkUp()
{
NS_LOG_FUNCTION(this);
m_linkUp = true;
m_linkChangeCallbacks();
}
void
EthernetNetDevice::SetIfIndex(const uint32_t index)
{
NS_LOG_FUNCTION(this);
m_ifIndex = index;
}
uint32_t
EthernetNetDevice::GetIfIndex() const
{
return m_ifIndex;
}
Ptr<Channel>
EthernetNetDevice::GetChannel() const
{
return m_channel;
}
void
EthernetNetDevice::SetAddress(Address address)
{
NS_LOG_FUNCTION(this << address);
m_address = Mac48Address::ConvertFrom(address);
}
Address
EthernetNetDevice::GetAddress() const
{
return m_address;
}
bool
EthernetNetDevice::IsLinkUp() const
{
NS_LOG_FUNCTION(this);
return m_linkUp;
}
void
EthernetNetDevice::AddLinkChangeCallback(Callback<void> callback)
{
NS_LOG_FUNCTION(this);
m_linkChangeCallbacks.ConnectWithoutContext(callback);
}
//
// This is a ethernet device, so every transmission is a broadcast to
// all of the devices on the channel.
//
bool
EthernetNetDevice::IsBroadcast() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetBroadcast() const
{
NS_LOG_FUNCTION(this);
return Mac48Address("ff:ff:ff:ff:ff:ff");
}
bool
EthernetNetDevice::IsMulticast() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetMulticast(Ipv4Address multicastGroup) const
{
NS_LOG_FUNCTION(this);
return Mac48Address("01:00:5e:00:00:00");
}
Address
EthernetNetDevice::GetMulticast(Ipv6Address addr) const
{
NS_LOG_FUNCTION(this << addr);
return Mac48Address("33:33:00:00:00:00");
}
bool
EthernetNetDevice::IsPointToPoint() const
{
NS_LOG_FUNCTION(this);
return true;
}
bool
EthernetNetDevice::IsBridge() const
{
NS_LOG_FUNCTION(this);
return false;
}
bool
EthernetNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype)
{
uint16_t vid = 65535;
uint8_t pcp = 0;
uint8_t dei = 0;
return SendFrom(packet, GetAddress(), dest, ethertype, vid, pcp, dei);
}
bool
EthernetNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
return SendFrom(packet, GetAddress(), dest, ethertype, vid, pcp, dei);
}
bool
EthernetNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype)
{
//Call by the switch net device with a packet with a EhternetHeader2 and EthernetTrailer
//Or by another SendFrom function that have already have a packet with header and trailer
// /!\ source, dest, and ethertype are not use in this function but are needed in
// the callback define by the netDevice class
//
// If IsLinkUp() is false it means there is no channel to send any packet
// over so we just hit the drop trace on the packet and return an error.
//
if (!IsLinkUp())
{
m_macTxDropTrace(packet);
return false;
}
//Add timestamp if first emission
TimestampTag tag(Simulator::Now());
if (!packet->FindFirstMatchingByteTag(tag))
{
packet->AddByteTag(tag);
}
m_macTxTrace(packet);
m_macTxAnimationTrace(packet, this);
Ptr<Packet> pCopy = packet->Copy();
EthernetHeader2 header;
pCopy->RemoveHeader(header);
uint8_t pcp = header.GetPcp();
NS_ASSERT_MSG(m_queues.size() > pcp, "Not enough fifos ( 0 to "<< m_queues.size()-1 <<") to handle this pcp priority ("<< unsigned(pcp) <<") on Node " <<Names::FindName(GetNode()));
m_FIFOStateSnifferTrace(m_queues, m_txMachineState==BUSY, packet);
//
// We should enqueue and dequeue the packet to hit the tracing hooks.
//
if (m_queues[pcp]->Enqueue(packet))
{
NS_LOG_INFO(Names::FindName(this) << " Enqueue pkt #"<<packet->GetUid() << " OK !");
//Add the event after all the already present at this exact instant to avoid
//the beginning of a transmission if a packet will be ready at the event just
//after but at the same time instant.
Simulator::Schedule(Time(0), &EthernetNetDevice::CheckForReadyPacket, this);
return true;
}
// Enqueue may fail (overflow)
m_macTxDropTrace(packet);
return false;
}
bool
EthernetNetDevice::SendFrom(Ptr<Packet> packet, const Address& source, const Address& dest, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
NS_LOG_FUNCTION(this << packet << dest << ethertype);
NS_LOG_LOGIC(Names::FindName(this) << " p=" << packet->GetUid() << ", dest=" << &dest);
// NS_LOG_INFO(Names::FindName(this) << " SendFrom : p=" << packet->GetUid() << ", dest=" << &dest << " at " << Simulator::Now());
if (vid == 65535){
AddHeader(packet, Mac48Address::ConvertFrom(dest), Mac48Address::ConvertFrom(source), ethertype);
}
else{
AddHeader(packet, Mac48Address::ConvertFrom(dest), Mac48Address::ConvertFrom(source), ethertype, vid, pcp, dei);
}
EthernetTrailer trailer;
trailer.EnableFcs(true);
trailer.CalcFcs(packet);
packet->AddTrailer(trailer);
return SendFrom(packet, source, dest, ethertype);
}
Ptr<Node>
EthernetNetDevice::GetNode() const
{
return m_node;
}
void
EthernetNetDevice::SetNode(Ptr<Node> node)
{
NS_LOG_FUNCTION(this);
m_node = node;
}
bool
EthernetNetDevice::NeedsArp() const
{
NS_LOG_FUNCTION(this);
return false;
}
void
EthernetNetDevice::SetReceiveCallback(NetDevice::ReceiveCallback cb)
{
m_rxCallback = cb;
}
void
EthernetNetDevice::SetPromiscReceiveCallback(NetDevice::PromiscReceiveCallback cb)
{
m_promiscCallback = cb;
}
bool
EthernetNetDevice::SupportsSendFrom() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetRemote() const
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_channel->GetNDevices() == 2);
for (std::size_t i = 0; i < m_channel->GetNDevices(); ++i)
{
Ptr<NetDevice> tmp = m_channel->GetDevice(i);
if (tmp != this)
{
return tmp->GetAddress();
}
}
NS_ASSERT(false);
// quiet compiler.
return Address();
}
bool
EthernetNetDevice::SetMtu(uint16_t mtu)
{
NS_LOG_FUNCTION(this << mtu);
m_mtu = mtu;
return true;
}
uint16_t
EthernetNetDevice::GetMtu() const
{
NS_LOG_FUNCTION(this);
return m_mtu;
}
}

View File

@@ -0,0 +1,464 @@
#ifndef ETHERNET_NET_DEVICE_H
#define ETHERNET_NET_DEVICE_H
#include "ns3/address.h"
#include "ns3/callback.h"
#include "ns3/data-rate.h"
#include "ns3/mac48-address.h"
#include "ns3/node.h"
#include "ns3/nstime.h"
#include "ns3/packet.h"
#include "ns3/ptr.h"
#include "ns3/queue-fwd.h"
#include "ns3/traced-callback.h"
#include "ns3/net-device.h"
#include <cstring>
namespace ns3
{
class EthernetChannel;
class ErrorModel;
/**
* \ingroup ethernet
* \class EthernetNetDevice
* \brief A Device for a Ethernet full duplex Link.
*
* This EthernetNetDevice class specializes the NetDevice abstract
* base class. Together with a EthernetChannel (and a peer
* EthernetNetDevice), the class models, with some level of
* abstraction, a full duplex ethernet link.
* Key parameters or objects that can be specified for this device
* include a queue, data rate, and interframe transmission gap (the
* propagation delay is set in the EthernetChannel).
*/
class EthernetNetDevice : public NetDevice
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* Construct a EthernetNetDevice
*
* This is the constructor for the EthernetNetDevice. It takes as a
* parameter a pointer to the Node to which this device is connected,
* as well as an optional DataRate object.
*/
EthernetNetDevice();
/**
* Destroy a EthernetNetDevice
*
* This is the destructor for the EthernetNetDevice.
*/
~EthernetNetDevice() override;
// Delete copy constructor and assignment operator to avoid misuse
EthernetNetDevice& operator=(const EthernetNetDevice&) = delete;
EthernetNetDevice(const EthernetNetDevice&) = delete;
/**
* Set the Data Rate used for transmission of packets. The data rate is
* set in the Attach () method from the corresponding field in the channel
* to which the device is attached. It can be overridden using this method.
*
* \param bps the data rate at which this object operates
*/
void SetDataRate(DataRate bps);
/**
* Attach the device to a channel.
*
* \param ch Ptr to the channel to which this object is being attached.
* \return true if the operation was successful (always true actually)
*/
bool Attach(Ptr<EthernetChannel> ch);
/**
* Attach a queue to the EthernetNetDevice.
*
* The EthernetNetDevice "owns" a queue that implements a queueing
* method such as DropTailQueue or RedQueue
*
* \param queue Ptr to the new queue.
*/
void SetQueue(Ptr<Queue<Packet>> queue);
/**
* Get a copy of the attached Queue.
*
* \param index Queue index
* \returns Ptr to the queue.
*/
Ptr<Queue<Packet>> GetQueue(uint8_t index) const;
/**
* Attach a receive ErrorModel to the EthernetNetDevice.
*
* The EthernetNetDevice may optionally include an ErrorModel in
* the packet receive chain.
*
* \param em Ptr to the ErrorModel.
*/
void SetReceiveErrorModel(Ptr<ErrorModel> em);
/**
* Receive a packet from a connected EthernetNetDevice.
*
* The EthernetNetDevice receives packets from its connected channel
* and forwards them up the protocol stack. This is the public method
* used by the channel to indicate that the last bit of a packet has
* arrived at the device.
*
* \param p Ptr to the received packet.
*/
virtual void Receive(Ptr<Packet> p);
// The remaining methods are documented in ns3::NetDevice*
void SetIfIndex(const uint32_t index) override;
uint32_t GetIfIndex() const override;
Ptr<Channel> GetChannel() const override;
void SetAddress(Address address) override;
Address GetAddress() const override;
bool SetMtu(const uint16_t mtu) override;
uint16_t GetMtu() const override;
bool IsLinkUp() const override;
void AddLinkChangeCallback(Callback<void> callback) override;
bool IsBroadcast() const override;
Address GetBroadcast() const override;
bool IsMulticast() const override;
Address GetMulticast(Ipv4Address multicastGroup) const override;
bool IsPointToPoint() const override;
bool IsBridge() const override;
bool Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype) override;
bool Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei);
virtual bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype) override;
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype,
uint16_t vid,
uint8_t pcp,
uint8_t dei);
Ptr<Node> GetNode() const override;
void SetNode(Ptr<Node> node) override;
bool NeedsArp() const override;
void SetReceiveCallback(NetDevice::ReceiveCallback cb) override;
Address GetMulticast(Ipv6Address addr) const override;
void SetPromiscReceiveCallback(PromiscReceiveCallback cb) override;
bool SupportsSendFrom() const override;
protected:
/**
* \brief Dispose of the object
*/
void DoDispose() override;
/**
* \returns the address of the remote device connected to this device
* through the ethernet channel.
*/
Address GetRemote() const;
/**
* Adds the necessary headers and trailers to a packet of data in order to
* respect the protocol implemented by the agent.
* \param p packet
* \param dst Dst mac address
* \param src Src mac address
* \param ethertype ethertype
*/
void AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype);
/**
* Adds the necessary headers to a packet of data in order to
* respect the protocol implemented by the agent.
* \param p packet
* \param dst Dst mac address
* \param src Src mac address
* \param ethertype ethertype
* \param vid vlain id
* \param pcp pcp field
* \param dei dei bit
*/
void AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei);
/**
* Start Sending a Packet Down the Wire.
*
* The TransmitStart method is the method that is used internally in the
* EthernetNetDevice to begin the process of sending a packet out on
* the channel. The corresponding method is called on the channel to let
* it know that the physical device this class represents has virtually
* started sending signals. An event is scheduled for the time at which
* the bits have been completely transmitted.
*
* \see EthernetChannel::TransmitStart ()
* \see TransmitComplete()
* \param p a reference to the packet to send
* \returns true if success, false on failure
*/
bool TransmitStart(Ptr<Packet> p);
/**
* Stop Sending a Packet Down the Wire and Begin the Interframe Gap.
*
* The TransmitComplete method is used internally to finish the process
* of sending a packet out on the channel.
*/
void TransmitComplete();
/**
* Find the nextEligible Queue for transimission
*
* \returns Int id to the queue.
*/
virtual int TransmitSelection();
virtual void CheckForReadyPacket();
/**
* \brief Make the link up and running
*
* It calls also the linkChange callback.
*/
void NotifyLinkUp();
/**
* Enumeration of the states of the transmit machine of the net device.
*/
enum TxMachineState
{
READY, /**< The transmitter is ready to begin transmission of a packet */
BUSY /**< The transmitter is busy transmitting a packet */
};
/**
* The state of the Net Device transmit state machine.
*/
TxMachineState m_txMachineState;
/**
* The data rate that the Net Device uses to simulate packet transmission
* timing.
*/
DataRate m_bps;
/**
* The preamble and start of frame delimiter gap that the Net Device uses to throttle packet
* transmission in bytes
*/
uint8_t m_preambleAndSFDGap;
/**
* The interframe gap that the Net Device uses to throttle packet
* transmission in bytes
*/
uint8_t m_interframeGap;
/**
* The EthernetChannel to which this EthernetNetDevice has been
* attached.
*/
Ptr<EthernetChannel> m_channel;
/**
* The Queues which this EthernetNetDevice uses as a packet source.
* Management of this Queues has been delegated to the EthernetNetDevice
* and it has the responsibility for deletion.
* \see class DropTailQueue
*/
std::vector<Ptr<Queue<Packet>>> m_queues;
/**
* Error model for receive packet events
*/
Ptr<ErrorModel> m_receiveErrorModel;
/**
* The trace source fired when packets come into the "top" of the device
* at the L3/L2 transition, before being queued for transmission.
*/
TracedCallback<Ptr<const Packet>> m_macTxTrace;
/**
* The trace source fired when packets coming into the "top" of the device
* at the L3/L2 transition are dropped before being queued for transmission.
*/
TracedCallback<Ptr<const Packet>> m_macTxDropTrace;
/**
* The trace source fired for packets successfully received by the device
* immediately before being forwarded up to higher layers (at the L2/L3
* transition). This is a promiscuous trace (which doesn't mean a lot here
* in the point-to-point device).
*/
TracedCallback<Ptr<const Packet>> m_macPromiscRxTrace;
/**
* The trace source fired for packets successfully received by the device
* immediately before being forwarded up to higher layers (at the L2/L3
* transition). This is a non-promiscuous trace (which doesn't mean a lot
* here in the point-to-point device).
*/
TracedCallback<Ptr<const Packet>> m_macRxTrace;
/**
* The trace source fired for packets successfully received by the device
* but are dropped before being forwarded up to higher layers (at the L2/L3
* transition).
*/
TracedCallback<Ptr<const Packet>> m_macRxDropTrace;
/**
* The trace source fired when a packet begins the transmission process on
* the medium.
*/
TracedCallback<Ptr<const Packet>> m_phyTxBeginTrace;
/**
* The trace source fired when a packet ends the transmission process on
* the medium.
*/
TracedCallback<Ptr<const Packet>> m_phyTxEndTrace;
/**
* The trace source fired when the phy layer drops a packet before it tries
* to transmit it.
*/
TracedCallback<Ptr<const Packet>> m_phyTxDropTrace;
/**
* The trace source fired when a packet begins the reception process from
* the medium -- when the simulated first bit(s) arrive.
*/
TracedCallback<Ptr<const Packet>> m_phyRxBeginTrace;
/**
* The trace source fired when a packet ends the reception process from
* the medium.
*/
TracedCallback<Ptr<const Packet>> m_phyRxEndTrace;
/**
* The trace source fired when the phy layer drops a packet it has received.
* This happens if the receiver is not enabled or the error model is active
* and indicates that the packet is corrupt.
*/
TracedCallback<Ptr<const Packet>> m_phyRxDropTrace;
/**
* The trace source fired when the packet is receive to compute latency.
*/
TracedCallback<Ptr<const Packet>> m_latencyTrace;
/**
* TracedCallback signature for packet transmission/reception events
* (for external visualyzer).
*
* \param [in] packet The packet being transmitted.
* \param [in] tx or rx NetDevice.
*/
typedef void (*MacTxAnimationCallback)(Ptr<const Packet> packet,
Ptr<NetDevice> device);
TracedCallback<Ptr<const Packet>, Ptr<NetDevice>> m_macTxAnimationTrace;
TracedCallback<Ptr<const Packet>, Ptr<NetDevice>> m_macRxAnimationTrace;
/**
* A trace source that emulates a non-promiscuous protocol sniffer connected
* to the device. Unlike your average everyday sniffer, this trace source
* will not fire on PACKET_OTHERHOST events.
*
* On the transmit size, this trace hook will fire after a packet is dequeued
* from the device queue for transmission. In Linux, for example, this would
* correspond to the point just before a device \c hard_start_xmit where
* \c dev_queue_xmit_nit is called to dispatch the packet to the PF_PACKET
* ETH_P_ALL handlers.
*
* On the receive side, this trace hook will fire when a packet is received,
* just before the receive callback is executed. In Linux, for example,
* this would correspond to the point at which the packet is dispatched to
* packet sniffers in \c netif_receive_skb.
*/
TracedCallback<Ptr<const Packet>> m_snifferTrace;
/**
* A trace source that emulates a promiscuous mode protocol sniffer connected
* to the device. This trace source fire on packets destined for any host
* just like your average everyday packet sniffer.
*
* On the transmit size, this trace hook will fire after a packet is dequeued
* from the device queue for transmission. In Linux, for example, this would
* correspond to the point just before a device \c hard_start_xmit where
* \c dev_queue_xmit_nit is called to dispatch the packet to the PF_PACKET
* ETH_P_ALL handlers.
*
* On the receive side, this trace hook will fire when a packet is received,
* just before the receive callback is executed. In Linux, for example,
* this would correspond to the point at which the packet is dispatched to
* packet sniffers in \c netif_receive_skb.
*/
TracedCallback<Ptr<const Packet>> m_promiscSnifferTrace;
TracedCallback<std::vector<Ptr<Queue<Packet>>>,
bool,
Ptr<const Packet>> m_FIFOStateSnifferTrace;
Ptr<Node> m_node; //!< Node owning this NetDevice
Mac48Address m_address; //!< Mac48Address of this NetDevice
NetDevice::ReceiveCallback m_rxCallback; //!< Receive callback
NetDevice::PromiscReceiveCallback m_promiscCallback; //!< Receive callback
// (promisc data)
uint32_t m_ifIndex; //!< Index of the interface
bool m_linkUp; //!< Identify if the link is up or not
TracedCallback<> m_linkChangeCallbacks; //!< Callback for the link change event
static const uint16_t DEFAULT_MTU = 1500; //!< Default MTU
/**
* \brief The Maximum Transmission Unit
*
* This corresponds to the maximum
* number of bytes that can be transmitted as seen from higher layers.
* This corresponds to the 1500 byte MTU size often seen on IP over
* Ethernet.
*/
uint32_t m_mtu;
Ptr<Packet> m_currentPkt; //!< Current packet processed
};
}
#endif /* ETHERNET_NET_DEVICE_H */

View File

@@ -0,0 +1,77 @@
#include "switch-channel.h"
#include "ns3/log.h"
/**
* \file
* \ingroup ethernet
* ns3::SwitchChannel implementation.
*/
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("SwitchChannel");
NS_OBJECT_ENSURE_REGISTERED(SwitchChannel);
TypeId
SwitchChannel::GetTypeId()
{
static TypeId tid = TypeId("ns3::SwitchChannel")
.SetParent<Channel>()
.SetGroupName("Ethernet")
.AddConstructor<SwitchChannel>();
return tid;
}
SwitchChannel::SwitchChannel()
: Channel()
{
NS_LOG_FUNCTION_NOARGS();
}
SwitchChannel::~SwitchChannel()
{
NS_LOG_FUNCTION_NOARGS();
for (auto iter = m_switchedChannels.begin(); iter != m_switchedChannels.end(); iter++)
{
*iter = nullptr;
}
m_switchedChannels.clear();
}
void
SwitchChannel::AddChannel(Ptr<Channel> switchedChannel)
{
m_switchedChannels.push_back(switchedChannel);
}
std::size_t
SwitchChannel::GetNDevices() const
{
uint32_t ndevices = 0;
for (auto iter = m_switchedChannels.begin(); iter != m_switchedChannels.end(); iter++)
{
ndevices += (*iter)->GetNDevices();
}
return ndevices;
}
Ptr<NetDevice>
SwitchChannel::GetDevice(std::size_t i) const
{
std::size_t ndevices = 0;
for (auto iter = m_switchedChannels.begin(); iter != m_switchedChannels.end(); iter++)
{
if ((i - ndevices) < (*iter)->GetNDevices())
{
return (*iter)->GetDevice(i - ndevices);
}
ndevices += (*iter)->GetNDevices();
}
return nullptr;
}
} // namespace ns3

View File

@@ -0,0 +1,59 @@
#ifndef SWITCH_CHANNEL_H
#define SWITCH_CHANNEL_H
#include "ns3/channel.h"
#include "ns3/net-device.h"
#include <vector>
/**
* \file
* \ingroup ethernet
* ns3::SwitchChannel declaration.
*/
namespace ns3
{
/**
* \ingroup bridge
*
* \brief Virtual channel implementation for bridges (BridgeNetDevice).
*
* Just like SwitchNetDevice aggregates multiple NetDevices,
* SwitchChannel aggregates multiple channels and make them appear as
* a single channel to upper layers.
* This class is greatly inspire form BridgeChannel
*/
class SwitchChannel : public Channel
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId();
SwitchChannel();
~SwitchChannel() override;
// Delete copy constructor and assignment operator to avoid misuse
SwitchChannel(const SwitchChannel&) = delete;
SwitchChannel& operator=(const SwitchChannel&) = delete;
/**
* Adds a channel to the bridged pool
* \param bridgedChannel the channel to add to the pool
*/
void AddChannel(Ptr<Channel> bridgedChannel);
// virtual methods implementation, from Channel
std::size_t GetNDevices() const override;
Ptr<NetDevice> GetDevice(std::size_t i) const override;
private:
std::vector<Ptr<Channel>> m_switchedChannels; //!< pool of switched channels
};
} // namespace ns3
#endif /* SWITCH_CHANNEL_H */

View File

@@ -0,0 +1,427 @@
#include "switch-net-device.h"
#include "ns3/boolean.h"
#include "ns3/channel.h"
#include "ns3/log.h"
#include "ns3/node.h"
#include "ns3/packet.h"
#include "ns3/uinteger.h"
#include "ns3/names.h"
#include "ns3/random-variable-stream.h"
#include "ns3/ethernet-header2.h"
#include "ns3/net-device.h"
/**
* \file
* \ingroup ethernet
* ns3::SwitchNetDevice implementation.
*/
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("SwitchNetDevice");
NS_OBJECT_ENSURE_REGISTERED(SwitchNetDevice);
TypeId
SwitchNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::SwitchNetDevice")
.SetParent<NetDevice>()
.SetGroupName("Ethernet")
.AddConstructor<SwitchNetDevice>()
.AddAttribute("Mtu",
"The MAC-level Maximum Transmission Unit",
UintegerValue(1500),
MakeUintegerAccessor(&SwitchNetDevice::SetMtu, &SwitchNetDevice::GetMtu),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MinForwardingLatency",
"The minimum latency experienced by the packet during forwarding",
TimeValue(Seconds(0)),
MakeTimeAccessor(&SwitchNetDevice::m_minForwardingLatency),
MakeTimeChecker())
.AddAttribute("MaxForwardingLatency",
"The maximun latency experienced by the packet during forwarding",
TimeValue(Seconds(0)),
MakeTimeAccessor(&SwitchNetDevice::m_maxForwardingLatency),
MakeTimeChecker())
.AddAttribute("MaxPortNumber",
"The maximum number of port",
UintegerValue(65535),
MakeUintegerAccessor(&SwitchNetDevice::m_maxPortNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxFdbEntryNumber",
"The maximum number of entry in the forwarding database",
UintegerValue(65535),
MakeUintegerAccessor(&SwitchNetDevice::m_maxFdbEntryNumber),
MakeUintegerChecker<uint16_t>());
return tid;
}
SwitchNetDevice::SwitchNetDevice()
: m_node(nullptr),
m_ifIndex(0)
{
NS_LOG_FUNCTION_NOARGS();
m_channel = CreateObject<SwitchChannel>();
}
SwitchNetDevice::~SwitchNetDevice()
{
NS_LOG_FUNCTION_NOARGS();
}
void
SwitchNetDevice::DoDispose()
{
NS_LOG_FUNCTION_NOARGS();
for (auto iter = m_ports.begin(); iter != m_ports.end(); iter++)
{
*iter = nullptr;
}
m_ports.clear();
m_forwardingTable.clear();
m_channel = nullptr;
m_node = nullptr;
NetDevice::DoDispose();
}
void
SwitchNetDevice::ReceiveFromDevice(Ptr<NetDevice> incomingPort,
Ptr<const Packet> packet,
uint16_t protocol,
const Address& src,
const Address& dst,
PacketType packetType)
{
NS_LOG_FUNCTION_NOARGS();
NS_LOG_DEBUG("UID is " << packet->GetUid());
Mac48Address src48 = Mac48Address::ConvertFrom(src);
Mac48Address dst48 = Mac48Address::ConvertFrom(dst);
if (!m_promiscRxCallback.IsNull())
{
m_promiscRxCallback(this, packet, protocol, src, dst, packetType);
}
switch (packetType)
{
case PACKET_HOST:
if (dst48 == m_address)
{
m_rxCallback(this, packet, protocol, src);
}
break;
case PACKET_MULTICAST:
case PACKET_BROADCAST:
m_rxCallback(this, packet, protocol, src);
ForwardBroadcast(incomingPort, packet, protocol, src48, dst48);
break;
case PACKET_OTHERHOST:
if (dst48 == m_address)
{
m_rxCallback(this, packet, protocol, src);
}
else
{
Forward(incomingPort, packet, protocol, src48, dst48);
}
break;
}
}
void
SwitchNetDevice::Forward(Ptr<NetDevice> incomingPort,
Ptr<const Packet> packet,
uint16_t protocol,
Mac48Address src,
Mac48Address dst)
{
NS_LOG_FUNCTION_NOARGS();
Ptr<Packet> originalPacket = packet->Copy();
EthernetHeader2 header;
originalPacket->RemoveHeader(header);
uint16_t vid = header.GetVid();
//NS_LOG_INFO(Names::FindName(this) << " Look in forwarding table for Pkt #" << packet->GetUid());
std::vector<Ptr<NetDevice>> outPorts = GetOutputPortsFromForwardingTable(dst, vid);
EventId e;
int s = outPorts.size();
for(int i=0; i < s; i++){
//Avoid sending on the incomingPort
if(outPorts[i] != incomingPort){
Ptr<UniformRandomVariable> randVar = CreateObject<UniformRandomVariable>();
Time forwardingLatency = NanoSeconds(randVar->GetValue(m_minForwardingLatency.GetNanoSeconds(), m_maxForwardingLatency.GetNanoSeconds()));
Simulator::Schedule(forwardingLatency, &NetDevice::SendFrom, outPorts[i], packet->Copy(), src, dst, protocol);
}
}
}
void
SwitchNetDevice::ForwardBroadcast(Ptr<NetDevice> incomingPort,
Ptr<const Packet> packet,
uint16_t protocol,
Mac48Address src,
Mac48Address dst)
{
NS_LOG_FUNCTION_NOARGS();
Forward(incomingPort, packet, protocol, src, dst);
}
std::vector<Ptr<NetDevice>>
SwitchNetDevice::GetOutputPortsFromForwardingTable(Mac48Address dest, uint16_t vid)
{
NS_LOG_FUNCTION(this);
int s = m_forwardingTable.size();
for(int i=0; i < s; i++){
if(m_forwardingTable[i].dest == dest and m_forwardingTable[i].vid==vid){
// NS_LOG_INFO(Names::FindName(this) << " Forwarding Table : Match on vid "<< vid << " for " << m_forwardingTable[i].associatedPorts.size() << " output port(s) ");
return m_forwardingTable[i].associatedPorts;
}
}
// NS_LOG_INFO(Names::FindName(this) << " Forwarding Table : No Match on vid "<< vid);
return {};
}
void
SwitchNetDevice::AddForwardingTableEntry(Mac48Address dest, uint16_t vid, std::vector<Ptr<NetDevice>> outPorts)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_forwardingTable.size() < m_maxFdbEntryNumber, "Trying to add more forwarding database entry than the " << m_maxFdbEntryNumber << " available.");
PortsToVidAssociation entry = {dest, vid, outPorts};
m_forwardingTable.insert(m_forwardingTable.end(), entry);
}
uint32_t
SwitchNetDevice::GetNSwitchPorts() const
{
NS_LOG_FUNCTION_NOARGS();
return m_ports.size();
}
Ptr<NetDevice>
SwitchNetDevice::GetSwitchPort(uint32_t n) const
{
NS_LOG_FUNCTION_NOARGS();
return m_ports[n];
}
void
SwitchNetDevice::AddSwitchPort(Ptr<NetDevice> switchPort)
{
NS_LOG_FUNCTION_NOARGS();
NS_ASSERT_MSG(m_ports.size() < m_maxPortNumber, "Trying to add more switch port than the " << m_maxPortNumber << " available.");
//NS_ASSERT(switchPort != this);
if (!Mac48Address::IsMatchingType(switchPort->GetAddress()))
{
NS_FATAL_ERROR("Device does not support eui 48 addresses: cannot be added to switch.");
}
if (!switchPort->SupportsSendFrom())
{
NS_FATAL_ERROR("Device does not support SendFrom: cannot be added to switch.");
}
if (m_address == Mac48Address())
{
m_address = Mac48Address::ConvertFrom(switchPort->GetAddress());
}
NS_LOG_DEBUG("RegisterProtocolHandler for " << switchPort->GetInstanceTypeId().GetName());
m_node->RegisterProtocolHandler(MakeCallback(&SwitchNetDevice::ReceiveFromDevice, this),
0,
switchPort,
true);
m_ports.push_back(switchPort);
m_channel->AddChannel(switchPort->GetChannel());
}
void
SwitchNetDevice::SetIfIndex(const uint32_t index)
{
NS_LOG_FUNCTION_NOARGS();
m_ifIndex = index;
}
uint32_t
SwitchNetDevice::GetIfIndex() const
{
NS_LOG_FUNCTION_NOARGS();
return m_ifIndex;
}
Ptr<Channel>
SwitchNetDevice::GetChannel() const
{
NS_LOG_FUNCTION_NOARGS();
return m_channel;
}
void
SwitchNetDevice::SetAddress(Address address)
{
NS_LOG_FUNCTION_NOARGS();
m_address = Mac48Address::ConvertFrom(address);
}
Address
SwitchNetDevice::GetAddress() const
{
NS_LOG_FUNCTION_NOARGS();
return m_address;
}
bool
SwitchNetDevice::SetMtu(const uint16_t mtu)
{
NS_LOG_FUNCTION_NOARGS();
m_mtu = mtu;
return true;
}
uint16_t
SwitchNetDevice::GetMtu() const
{
NS_LOG_FUNCTION_NOARGS();
return m_mtu;
}
bool
SwitchNetDevice::IsLinkUp() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
void
SwitchNetDevice::AddLinkChangeCallback(Callback<void> callback)
{
}
bool
SwitchNetDevice::IsBroadcast() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
Address
SwitchNetDevice::GetBroadcast() const
{
NS_LOG_FUNCTION_NOARGS();
return Mac48Address("ff:ff:ff:ff:ff:ff");
}
bool
SwitchNetDevice::IsMulticast() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
Address
SwitchNetDevice::GetMulticast(Ipv4Address multicastGroup) const
{
NS_LOG_FUNCTION(this << multicastGroup);
Mac48Address multicast = Mac48Address::GetMulticast(multicastGroup);
return multicast;
}
bool
SwitchNetDevice::IsPointToPoint() const
{
NS_LOG_FUNCTION_NOARGS();
return false;
}
bool
SwitchNetDevice::IsBridge() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
bool
SwitchNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber)
{
NS_LOG_FUNCTION_NOARGS();
return SendFrom(packet, m_address, dest, protocolNumber);
}
bool
SwitchNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t protocolNumber)
{
NS_LOG_FUNCTION_NOARGS();
Mac48Address src = Mac48Address::ConvertFrom(source);
Mac48Address dst = Mac48Address::ConvertFrom(dest);
Ptr<Packet> originalPacket = packet->Copy();
EthernetHeader2 header;
originalPacket->RemoveHeader(header);
uint16_t vid = header.GetVid();
std::vector<Ptr<NetDevice>> outPorts = GetOutputPortsFromForwardingTable(dst, vid);
int s = outPorts.size();
for(int i=0; i < s; i++){
outPorts[i]->SendFrom(packet->Copy(), src, dst, protocolNumber);
}
return true;
}
Ptr<Node>
SwitchNetDevice::GetNode() const
{
NS_LOG_FUNCTION_NOARGS();
return m_node;
}
void
SwitchNetDevice::SetNode(Ptr<Node> node)
{
NS_LOG_FUNCTION_NOARGS();
m_node = node;
}
bool
SwitchNetDevice::NeedsArp() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
void
SwitchNetDevice::SetReceiveCallback(NetDevice::ReceiveCallback cb)
{
NS_LOG_FUNCTION_NOARGS();
m_rxCallback = cb;
}
void
SwitchNetDevice::SetPromiscReceiveCallback(NetDevice::PromiscReceiveCallback cb)
{
NS_LOG_FUNCTION_NOARGS();
m_promiscRxCallback = cb;
}
bool
SwitchNetDevice::SupportsSendFrom() const
{
NS_LOG_FUNCTION_NOARGS();
return true;
}
Address
SwitchNetDevice::GetMulticast(Ipv6Address addr) const
{
NS_LOG_FUNCTION(this << addr);
return Mac48Address::GetMulticast(addr);
}
} // namespace ns3

View File

@@ -0,0 +1,220 @@
#ifndef SWITCH_NET_DEVICE_H
#define SWITCH_NET_DEVICE_H
#include "switch-channel.h"
#include "ns3/net-device.h"
#include "ns3/mac48-address.h"
#include "ns3/net-device.h"
#include "ns3/nstime.h"
#include "ns3/simulator.h"
#include <map>
#include <stdint.h>
#include <string>
/**
* \file
* \ingroup ethernet
* ns3::SwitchNetDevice declaration.
*/
namespace ns3
{
class Node;
/**
* \brief a virtual net device that bridges multiple LAN segments
*
* The SwitchNetDevice object is a "virtual" netdevice that aggregates
* multiple "real" netdevices and implements the data plane forwarding
* part of IEEE 802.1D. By adding a SwitchNetDevice to a Node, it
* will act as a "switch" to multiple LAN segments.
*
* \attention The switch netdevice don't implement a "Learning bridge" algorithm
* or Spanning Tree Protocol as described in 802.1D
*
* \attention Switching is designed to work only with Ethernet NetDevices
* modelling IEEE 802-style technologies, such as EthernetNetDevice
*/
/**
* \ingroup ethernet
* \brief a virtual net device that bridges multiple LAN segments
*/
class SwitchNetDevice : public NetDevice
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId();
SwitchNetDevice();
~SwitchNetDevice() override;
// Delete copy constructor and assignment operator to avoid misuse
SwitchNetDevice(const SwitchNetDevice&) = delete;
SwitchNetDevice& operator=(const SwitchNetDevice&) = delete;
/**
* \brief Add a 'port' to a switch device
* \param switchPort the NetDevice to add
*
* This method adds a new switch port to a SwitchNetDevice, so that
* the new switch port NetDevice becomes part of the switch and L2
* frames start being forwarded to/from this NetDevice.
*
* \attention The netdevice that is being added as switch port must
* _not_ have an IP address. In order to add IP connectivity to a
* bridging node you must enable IP on the SwitchNetDevice itself,
* never on its port netdevices.
*/
void AddSwitchPort(Ptr<NetDevice> switchPort);
/**
* \brief Gets the number of switched 'ports', i.e., the NetDevices currently switched.
*
* \return the number of switched ports.
*/
uint32_t GetNSwitchPorts() const;
/**
* \brief Gets the n-th switched port.
* \param n the port index
* \return the n-th switched NetDevice
*/
Ptr<NetDevice> GetSwitchPort(uint32_t n) const;
// inherited from NetDevice base class.
void SetIfIndex(const uint32_t index) override;
uint32_t GetIfIndex() const override;
Ptr<Channel> GetChannel() const override;
void SetAddress(Address address) override;
Address GetAddress() const override;
bool SetMtu(const uint16_t mtu) override;
uint16_t GetMtu() const override;
bool IsLinkUp() const override;
void AddLinkChangeCallback(Callback<void> callback) override;
bool IsBroadcast() const override;
Address GetBroadcast() const override;
bool IsMulticast() const override;
Address GetMulticast(Ipv4Address multicastGroup) const override;
bool IsPointToPoint() const override;
bool IsBridge() const override;
bool Send(Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber) override;
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t protocolNumber) override;
Ptr<Node> GetNode() const override;
void SetNode(Ptr<Node> node) override;
bool NeedsArp() const override;
void SetReceiveCallback(NetDevice::ReceiveCallback cb) override;
void SetPromiscReceiveCallback(NetDevice::PromiscReceiveCallback cb) override;
bool SupportsSendFrom() const override;
Address GetMulticast(Ipv6Address addr) const override;
/**
* \brief Add a entry to the forwarding table
* \param dest destination mac
* \param vid Vlan ID
* \param outPorts vector of output port
*/
void AddForwardingTableEntry(Mac48Address dest,
uint16_t vid,
std::vector<Ptr<NetDevice>> outPorts);
protected:
void DoDispose() override;
/**
* \brief Receives a packet from one switched port.
* \param device the originating port
* \param packet the received packet
* \param protocol the packet protocol (e.g., Ethertype)
* \param source the packet source
* \param destination the packet destination
* \param packetType the packet type (e.g., host, broadcast, etc.)
*/
void ReceiveFromDevice(Ptr<NetDevice> device,
Ptr<const Packet> packet,
uint16_t protocol,
const Address& source,
const Address& destination,
PacketType packetType);
/**
* \brief Forwards a packet
* \param incomingPort the packet incoming port
* \param packet the packet
* \param protocol the packet protocol (e.g., Ethertype)
* \param src the packet source
* \param dst the packet destination
*/
void Forward(Ptr<NetDevice> incomingPort,
Ptr<const Packet> packet,
uint16_t protocol,
Mac48Address src,
Mac48Address dst);
/**
* \brief Forwards a broadcast or a multicast packet
* \param incomingPort the packet incoming port
* \param packet the packet
* \param protocol the packet protocol (e.g., Ethertype)
* \param src the packet source
* \param dst the packet destination
*/
void ForwardBroadcast(Ptr<NetDevice> incomingPort,
Ptr<const Packet> packet,
uint16_t protocol,
Mac48Address src,
Mac48Address dst);
/**
* \brief Get the output ports from the forwarding table
* \param dest destination mac
* \param vid Vlan ID
* \return output ports
*/
std::vector<Ptr<NetDevice>> GetOutputPortsFromForwardingTable(Mac48Address dest,
uint16_t vid);
private:
NetDevice::ReceiveCallback m_rxCallback; //!< receive callback
NetDevice::PromiscReceiveCallback m_promiscRxCallback; //!< promiscuous receive callback
Mac48Address m_address; //!< MAC address of the NetDevice
/**
* \ingroup ethernet
* Structure holding the association between ports and vid for a mac destination address
*/
struct PortsToVidAssociation
{
Mac48Address dest;
uint16_t vid;
std::vector<Ptr<NetDevice>> associatedPorts;
};
std::vector<PortsToVidAssociation> m_forwardingTable; //!< Container for forwarding table
Ptr<Node> m_node; //!< node owning this NetDevice
Ptr<SwitchChannel> m_channel; //!< virtual bridged channel
std::vector<Ptr<NetDevice>> m_ports; //!< switched ports
uint32_t m_ifIndex; //!< Interface index
uint16_t m_mtu; //!< MTU of the switched NetDevice
Time m_minForwardingLatency;
Time m_maxForwardingLatency;
uint16_t m_maxPortNumber;
uint16_t m_maxFdbEntryNumber;
};
} // namespace ns3
#endif /* SWITCH_NET_DEVICE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
#! /usr/bin/env python3
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("ethernet-point2point", "True", "True"),
("ethernet-point2point-withPropagationDelay", "True", "True"),
("ethernet-point2point-withSmallFifo", "True", "True"),
("ethernet-point2point-10Gb", "True", "True"),
("ethernet-switched", "True", "True"),
("ethernet-switched-withLatencyCallback", "True", "True"),
("ethernet-switched-withPcap", "True", "True"),
("ethernet-switched-withRxTxCallback", "True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = []

View File

@@ -0,0 +1,24 @@
check_include_file_cxx(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
add_definitions(-DHAVE_STDINT_H)
endif()
set(examples_as_tests_sources)
if(${ENABLE_EXAMPLES})
set(examples_as_tests_sources
#test/real-device-examples-test-suite.cc
)
endif()
build_lib(
LIBNAME real-device
SOURCE_FILES model/evb-lan9668.cc
helper/real-device-helper.cc
HEADER_FILES model/evb-lan9668.h
helper/real-device-helper.h
LIBRARIES_TO_LINK ${libcore}
${libethernet}
${libtsn}
TEST_SOURCES test/real-device-test-suite.cc
${examples_as_tests_sources}
)

View File

@@ -0,0 +1,22 @@
set(base_examples
evb-lan9668-sp
evb-lan9668-cbs
evb-lan9668-tas
evb-lan9668-psfp
evb-lan9668-frer
)
foreach(
example
${base_examples}
)
build_lib_example(
NAME ${example}
SOURCE_FILES ${example}.cc
LIBRARIES_TO_LINK ${libtsn}
${libcore}
${libnetwork}
${libtraffic-generator}
${libethernet}
${libreal-device}
)
endforeach()

View File

@@ -0,0 +1,158 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/evb-lan9668.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-match-recovery-function.h"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 4ES connected to 1SW in a 1Gb/s full duplex link as follow.
* ES1 === \ / === ES2
* SW
* ES3 === / \ === ES4
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("EvbLan9668", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four end stations
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES1", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES2", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES3", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("ES4", n4);
//Create and add a netDevices to each end station
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ES1e#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
Names::Add("ES2#01", net2);
Ptr<TsnNetDevice> net3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3);
Names::Add("ES3#01", net3);
Ptr<TsnNetDevice> net4 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4);
Names::Add("ES4#01", net4);
//Allocate a Mac address to the end station
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
net4->SetAddress(Mac48Address::Allocate());
//Create the switch
Ptr<EvbLan9668> lan9668 = CreateObject<EvbLan9668>("SW");
lan9668->AddCbs(2, 4, DataRate("1Mb/s"), DataRate("1Gb/s")); //(uint32_t port_id, uint32_t queue_id, DataRate iddle_slope, DataRate port_transmit_rate)
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net1->Attach(l0);
lan9668->GetPort(1)->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2->Attach(l1);
lan9668->GetPort(2)->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3->Attach(l2);
lan9668->GetPort(3)->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4->Attach(l3);
lan9668->GetPort(4)->Attach(l3);
//Create and add eight FIFO on each end station net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table entry
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {2});
lan9668->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 10, {2, 3, 4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 11, {1});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net4->GetAddress()), 12, {4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 13, {2});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 14, {2});
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net1);
app0->SetAttribute("BurstSize", UintegerValue(3));
app0->SetAttribute("PayloadSize", UintegerValue(1478));
app0->SetAttribute("Period", TimeValue(MilliSeconds(100)));
app0->SetAttribute("PCP", UintegerValue(4));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
n1->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(200));
//Callback to display the packet transmitted and received log
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n2) + ":" + Names::FindName(net2)));
//Execute the simulation
Simulator::Stop(MilliSeconds(300));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,217 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/evb-lan9668.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-match-recovery-function.h"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 4ES connected to 2SW in a 1Gb/s full duplex link as follow.
* ES1 === \ /===\ / === ES2
* SW1 SW2
* ES3 === / \===/ \ === ES4
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log pkt elimination due to FRER
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("EvbLan9668", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four end stations
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES1", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES2", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES3", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("ES4", n4);
//Create and add a netDevices to each end station
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ES1e#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
Names::Add("ES2#01", net2);
Ptr<TsnNetDevice> net3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3);
Names::Add("ES3#01", net3);
Ptr<TsnNetDevice> net4 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4);
Names::Add("ES4#01", net4);
//Allocate a Mac address to the end station
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
net4->SetAddress(Mac48Address::Allocate());
//Create the switch
Ptr<EvbLan9668> lan9668_1 = CreateObject<EvbLan9668>("SW1");
Ptr<EvbLan9668> lan9668_2 = CreateObject<EvbLan9668>("SW2");
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net1->Attach(l0);
lan9668_1->GetPort(1)->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2->Attach(l1);
lan9668_2->GetPort(2)->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3->Attach(l2);
lan9668_2->GetPort(1)->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4->Attach(l3);
lan9668_1->GetPort(2)->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
l4->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
lan9668_1->GetPort(3)->Attach(l4);
lan9668_2->GetPort(3)->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
l5->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
lan9668_1->GetPort(4)->Attach(l5);
lan9668_2->GetPort(4)->Attach(l5);
//Create and add eight FIFO on each end station net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table entry
lan9668_1->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {3,4});
lan9668_2->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {2});
//SW1 Stream identification and FRER configuration (replication)
//Stream identification
uint32_t streamHandle = 1;
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
sif0->SetAttribute("VlanID", UintegerValue(10));
sif0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
lan9668_1->AddNullStreamIdentificationFunction(streamHandle, sif0, {1}, {}, {}, {});
//Sequencing : Sequence generation
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({streamHandle});
lan9668_1->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqEnc0->SetAttribute("Active", BooleanValue(true));
seqEnc0->SetStreamHandle({streamHandle});
lan9668_1->AddSequenceEncodeDecodeFunction(seqEnc0, 1);
//SW2 Stream identification adn FRER configuration (elimination)
//Stream identification
Ptr<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
sif1->SetAttribute("VlanID", UintegerValue(10));
sif1->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
lan9668_2->AddNullStreamIdentificationFunction(streamHandle, sif1, {}, {}, {2}, {});
//Sequence Decode
Ptr<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc1->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqEnc1->SetAttribute("Active", BooleanValue(false));
seqEnc1->SetStreamHandle({streamHandle});
lan9668_2->AddSequenceEncodeDecodeFunction(seqEnc1, 2);
//Sequencing : Sequence recovery
Ptr<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
seqfreco0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqfreco0->SetAttribute("TakeNoSequence", BooleanValue(false));
seqfreco0->SetAttribute("IndividualRecovery", BooleanValue(false));
seqfreco0->SetStreamHandle({streamHandle});
//Sequencing : Sequence recovery : recovery function
Ptr<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
recf0->SetAttribute("ResetTimer", TimeValue(Seconds(1)));
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
lan9668_2->AddSequenceRecoveryFunction(seqfreco0, recf0, latf0, {2});
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net1);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1478));
app0->SetAttribute("Period", TimeValue(MilliSeconds(100)));
app0->SetAttribute("PCP", UintegerValue(4));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
n1->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(200));
//Callback to display the packet transmitted and received log
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n2) + ":" + Names::FindName(net2)));
//Callback to display elimination event
lan9668_2->GetPort(2)->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(lan9668_2->GetPort(2))));
//Execute the simulation
Simulator::Stop(MilliSeconds(300));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,194 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/evb-lan9668.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-match-recovery-function.h"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 4ES connected to 1SW in a 1Gb/s full duplex link as follow.
* ES1 === \ / === ES2
* SW
* ES3 === / \ === ES4
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//Callbacks to log pkt drop
static void
MaxSDUSizeFilterDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : Packet #"<< p->GetUid() <<" was dropped by MaxSDUSizeFilter");
}
static void
REDFrameDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : Packet #"<< p->GetUid() <<" was dropped by Flow Meter");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("EvbLan9668", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four end stations
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES1", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES2", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES3", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("ES4", n4);
//Create and add a netDevices to each end station
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ES1e#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
Names::Add("ES2#01", net2);
Ptr<TsnNetDevice> net3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3);
Names::Add("ES3#01", net3);
Ptr<TsnNetDevice> net4 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4);
Names::Add("ES4#01", net4);
//Allocate a Mac address to the end station
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
net4->SetAddress(Mac48Address::Allocate());
//Create the switch
Ptr<EvbLan9668> lan9668 = CreateObject<EvbLan9668>("SW");
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net1->Attach(l0);
lan9668->GetPort(1)->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2->Attach(l1);
lan9668->GetPort(2)->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3->Attach(l2);
lan9668->GetPort(3)->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4->Attach(l3);
lan9668->GetPort(4)->Attach(l3);
//Create and add eight FIFO on each end station net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table entry
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {2});
lan9668->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 10, {2, 3, 4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 11, {1});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net4->GetAddress()), 12, {4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 13, {2});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 14, {2});
//Add a null stream identification function on the switch for psfp
Ptr<NullStreamIdentificationFunction> sif = CreateObject<NullStreamIdentificationFunction>();
sif->SetAttribute("VlanID", UintegerValue(10));
sif->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
lan9668->AddNullStreamIdentificationFunction(112, sif, {1}, {}, {}, {});
//Configure PSFP on the switch
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
sfi0->SetAttribute("StreamHandle", IntegerValue(112));
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard (like in PSFP MIB)
sfi0->SetAttribute("MaxSDUSize", UintegerValue(1500));
lan9668->AddStreamFilter(sfi0);
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
fm0->SetAttribute("CIR", DataRateValue(DataRate("40Mb/s")));
fm0->SetAttribute("CBS", UintegerValue(1500));
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
uint16_t fmid = lan9668->AddFlowMeter(fm0);
sfi0->AddFlowMeterInstanceId(fmid);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net1);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1478));
app0->SetAttribute("Period", TimeValue(MilliSeconds(100)));
app0->SetAttribute("PCP", UintegerValue(4));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
n1->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(200));
//Callback to display the packet transmitted and received log
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n2) + ":" + Names::FindName(net2)));
//Callback related to PSFP event
lan9668->GetPort(1)->TraceConnectWithoutContext("MaxSDUSizeFilterDrop", MakeBoundCallback(&MaxSDUSizeFilterDrop, Names::FindName(lan9668->GetPort(1))));
lan9668->GetPort(1)->TraceConnectWithoutContext("REDFrameDrop", MakeBoundCallback(&REDFrameDrop, Names::FindName(lan9668->GetPort(1))));
//Execute the simulation
Simulator::Stop(MilliSeconds(300));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,156 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/evb-lan9668.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-match-recovery-function.h"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 4ES connected to 1SW in a 1Gb/s full duplex link as follow.
* ES1 === \ / === ES2
* SW
* ES3 === / \ === ES4
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("EvbLan9668", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four end stations
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES1", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES2", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES3", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("ES4", n4);
//Create and add a netDevices to each end station
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ES1e#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
Names::Add("ES2#01", net2);
Ptr<TsnNetDevice> net3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3);
Names::Add("ES3#01", net3);
Ptr<TsnNetDevice> net4 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4);
Names::Add("ES4#01", net4);
//Allocate a Mac address to the end station
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
net4->SetAddress(Mac48Address::Allocate());
//Create the switch
Ptr<EvbLan9668> lan9668 = CreateObject<EvbLan9668>("SW");
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net1->Attach(l0);
lan9668->GetPort(1)->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2->Attach(l1);
lan9668->GetPort(2)->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3->Attach(l2);
lan9668->GetPort(3)->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4->Attach(l3);
lan9668->GetPort(4)->Attach(l3);
//Create and add eight FIFO on each end station net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table entry
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {2});
lan9668->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 10, {2, 3, 4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 11, {1});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net4->GetAddress()), 12, {4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 13, {2});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 14, {2});
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net1);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1478));
app0->SetAttribute("Period", TimeValue(MilliSeconds(100)));
app0->SetAttribute("PCP", UintegerValue(4));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
n1->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(200));
//Callback to display the packet transmitted and received log
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n2) + ":" + Names::FindName(net2)));
//Execute the simulation
Simulator::Stop(MilliSeconds(300));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,160 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/evb-lan9668.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-match-recovery-function.h"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 4ES connected to 1SW in a 1Gb/s full duplex link as follow.
* ES1 === \ / === ES2
* SW
* ES3 === / \ === ES4
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("EvbLan9668", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four end stations
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES1", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES2", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES3", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("ES4", n4);
//Create and add a netDevices to each end station
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ES1e#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2);
Names::Add("ES2#01", net2);
Ptr<TsnNetDevice> net3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3);
Names::Add("ES3#01", net3);
Ptr<TsnNetDevice> net4 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4);
Names::Add("ES4#01", net4);
//Allocate a Mac address to the end station
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
net4->SetAddress(Mac48Address::Allocate());
//Create the switch
Ptr<EvbLan9668> lan9668 = CreateObject<EvbLan9668>("SW");
lan9668->AddGclEntry(2,MilliSeconds(200), 1); //uint32_t port_id, Time interval, uint8_t state
lan9668->AddGclEntry(2,MilliSeconds(200), 16);
lan9668->StartTas();
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net1->Attach(l0);
lan9668->GetPort(1)->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2->Attach(l1);
lan9668->GetPort(2)->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3->Attach(l2);
lan9668->GetPort(3)->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4->Attach(l3);
lan9668->GetPort(4)->Attach(l3);
//Create and add eight FIFO on each end station net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table entry
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {2});
lan9668->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 10, {2, 3, 4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net1->GetAddress()), 11, {1});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net4->GetAddress()), 12, {4});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 13, {2});
lan9668->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 14, {2});
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net1);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1478));
app0->SetAttribute("Period", TimeValue(MilliSeconds(100)));
app0->SetAttribute("PCP", UintegerValue(4));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("Address", AddressValue(Mac48Address::ConvertFrom(net2->GetAddress())));
n1->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(200));
//Callback to display the packet transmitted and received log
net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
net2->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n2) + ":" + Names::FindName(net2)));
//Execute the simulation
Simulator::Stop(MilliSeconds(300));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,8 @@
#include "real-device-helper.h"
namespace ns3
{
/* ... */
}

View File

@@ -0,0 +1,16 @@
#ifndef REAL_DEVICE_HELPER_H
#define REAL_DEVICE_HELPER_H
#include "ns3/evb-lan9668.h"
namespace ns3
{
// Each class should be documented using Doxygen,
// and have an \ingroup real-device directive
/* ... */
}
#endif /* REAL_DEVICE_HELPER_H */

View File

@@ -0,0 +1,267 @@
#include "evb-lan9668.h"
#include "ns3/names.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/cbs.h"
#include "ns3/tas.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EvbLan9668");
NS_OBJECT_ENSURE_REGISTERED(EvbLan9668);
TypeId
EvbLan9668::GetTypeId()
{
static TypeId tid =
TypeId("ns3::EvbLan9668")
.SetParent<Object>()
.SetGroupName("real-device")
.AddConstructor<EvbLan9668>();
return tid;
}
EvbLan9668::EvbLan9668()
{
NS_LOG_FUNCTION(this);
m_node = CreateObject<TsnNode>();
m_node->AddClock(CreateObject<Clock>()); //Add perfect clock
for (int i=0; i<m_portNumber; ++i){
Ptr<TsnNetDevice> net = CreateObject<TsnNetDevice>();
for (int j=0; j<m_queuesPerPort; ++j){
Ptr<DropTailQueue<Packet>> q = CreateObject<DropTailQueue<Packet>>();
q->SetAttribute("MaxSize", QueueSizeValue(m_fifoSize));
net->SetQueue(q);
}
m_node->AddDevice(net);
m_net_devices.insert(m_net_devices.end(), net);
}
m_switch_net_device = CreateObject<SwitchNetDevice>();
m_switch_net_device->SetAddress(Mac48Address::Allocate());
m_node->AddDevice(m_switch_net_device);
for (int i = 0; i < (int)m_net_devices.size(); i++){
m_switch_net_device->AddSwitchPort(m_net_devices[i]);
}
m_node->AddDevice(m_switch_net_device);
SetHardwareLimits();
}
EvbLan9668::EvbLan9668(std::string name): EvbLan9668()
{
NS_LOG_FUNCTION(this);
Names::Add(name, m_node);
for (int i = 0; i < (int)m_net_devices.size(); i++){
Names::Add(name + "-" + std::to_string(i) , m_net_devices[i]);
}
}
EvbLan9668::~EvbLan9668()
{
NS_LOG_FUNCTION(this);
}
void
EvbLan9668::SetHardwareLimits()
{
NS_LOG_FUNCTION(this);
//FDB
m_switch_net_device->SetAttribute("MaxPortNumber", UintegerValue(m_portNumber));
m_switch_net_device->SetAttribute("MaxFdbEntryNumber", UintegerValue(m_maxFdbEntryNumber));
m_switch_net_device->SetAttribute("MinForwardingLatency", TimeValue(m_minForwardingLatency));
m_switch_net_device->SetAttribute("MaxForwardingLatency", TimeValue(m_maxForwardingLatency));
//TAS
for (int i = 0; i < (int)m_net_devices.size(); i++){
m_net_devices[i]->GetTas()->SetAttribute("MaxGclEntryNumber", UintegerValue(m_maxGclEntryNumber));
m_net_devices[i]->GetTas()->SetAttribute("MaxGclCycleDuration", TimeValue(m_maxGclCycleDuration));
m_net_devices[i]->GetTas()->SetAttribute("MaxGclTimeInterval", TimeValue(m_maxGclTimeInterval));
}
//Stream identification
m_node->SetAttribute("MaxSidEntryNumber", UintegerValue(m_maxSidEntryNumber));
//PSFP
m_node->SetAttribute("MaxPsfpFilterEntryNumber", UintegerValue(m_maxPsfpFilterEntryNumber));
m_node->SetAttribute("MaxPsfpStreamGateEntryNumber", UintegerValue(m_maxPsfpStreamGateEntryNumber));
m_node->SetAttribute("MaxPsfpFlowMeterEntryNumber", UintegerValue(m_maxPsfpFlowMeterEntryNumber));
//FRER
m_node->SetAttribute("MaxFrerSeqGenEntryNumber", UintegerValue(m_maxFrerSeqGenEntryNumber));
m_node->SetAttribute("MaxFrerSeqRcvyEntryNumber", UintegerValue(m_maxFrerSeqRcvyEntryNumber));
m_node->SetAttribute("MaxFrerSeqEncEntryNumber", UintegerValue(m_maxFrerSeqEncEntryNumber));
}
Ptr<TsnNetDevice>
EvbLan9668::GetPort(int port_id)
{
NS_LOG_FUNCTION(this);
NS_ASSERT(port_id < (int) m_net_devices.size());
return m_net_devices[port_id];
}
void
EvbLan9668::SetPortDatarate(int port_id, DataRate d)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(d == DataRate("10Mb/s") || d == DataRate("100Mb/s") || d == DataRate("1Gb/s"), "Trying to use a datarate not supported on this device (i.e 10Mb/s, 100Mb/s and 1Gb/s)");
GetPort(port_id)->SetAttribute("DataRate", DataRateValue(d));
}
void
EvbLan9668::AddForwardingTableEntry(Mac48Address dest, uint16_t vlan_id, std::vector<uint32_t> output_port_ids){
NS_LOG_FUNCTION(this);
std::vector<Ptr<NetDevice>> output_ports = {};
for (int i = 0; i < (int)output_port_ids.size(); i++){
output_ports.insert(output_ports.end(), GetPort(output_port_ids[i]));
}
m_switch_net_device->AddForwardingTableEntry(dest, vlan_id, output_ports);
}
void
EvbLan9668::AddCbs(uint32_t port_id, uint32_t queue_id, DataRate iddle_slope, DataRate port_transmit_rate)
{
NS_LOG_FUNCTION(this);
NS_ASSERT(queue_id < m_queuesPerPort);
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(GetPort(port_id));
cbs->SetAttribute("IdleSlope", DataRateValue(iddle_slope));
cbs->SetAttribute("MaxIdleSlope", DataRateValue(m_maxIddleSlope));
cbs->SetAttribute("portTransmitRate", DataRateValue(port_transmit_rate));
cbs->SetAttribute("MinLatencyOverhead", TimeValue(m_minCBSLatencyOverhead));
cbs->SetAttribute("MaxLatencyOverhead", TimeValue(m_maxCBSLatencyOverhead));
Ptr<DropTailQueue<Packet>> q = CreateObject<DropTailQueue<Packet>>();
q->SetAttribute("MaxSize", QueueSizeValue(m_fifoSize));
GetPort(port_id)->UpdateQueue(queue_id, q, cbs);
}
void
EvbLan9668::AddGclEntry(uint32_t port_id, Time interval, uint8_t state)
{
NS_LOG_FUNCTION(this);
GetPort(port_id)->AddGclEntry(interval, state);
}
void
EvbLan9668::StartTas()
{
NS_LOG_FUNCTION(this);
for (int i = 0; i < (int)m_net_devices.size(); i++){
m_net_devices[i]->StartTas();
}
}
void
EvbLan9668::AddNullStreamIdentificationFunction(
uint32_t streamHandle,
Ptr<NullStreamIdentificationFunction> streamIdentificationFunction,
std::vector<uint32_t> outFacInputPortIds,
std::vector<uint32_t> inFacInputPortIds,
std::vector<uint32_t> inFacOutputPortIds,
std::vector<uint32_t> outFacOutputPortIds)
{
NS_LOG_FUNCTION(this);
std::vector<Ptr<TsnNetDevice>> outFacInputPortList = {};
for (int i = 0; i < (int)outFacInputPortIds.size(); i++){
outFacInputPortList.insert(outFacInputPortList.end(), GetPort(outFacInputPortIds[i]));
}
std::vector<Ptr<TsnNetDevice>> inFacInputPortList = {};
for (int i = 0; i < (int)inFacInputPortIds.size(); i++){
inFacInputPortList.insert(inFacInputPortList.end(), GetPort(inFacInputPortIds[i]));
}
std::vector<Ptr<TsnNetDevice>> inFacOutputPortList = {};
for (int i = 0; i < (int)inFacOutputPortIds.size(); i++){
inFacOutputPortList.insert(inFacOutputPortList.end(), GetPort(inFacOutputPortIds[i]));
}
std::vector<Ptr<TsnNetDevice>> outFacOutputPortList = {};
for (int i = 0; i < (int)outFacOutputPortIds.size(); i++){
outFacOutputPortList.insert(outFacOutputPortList.end(), GetPort(outFacOutputPortIds[i]));
}
streamIdentificationFunction->SetAttribute("MinLatencyOverhead", TimeValue(m_minNullSIDLatencyOverhead));
streamIdentificationFunction->SetAttribute("MaxLatencyOverhead", TimeValue(m_maxNullSIDLatencyOverhead));
m_node->AddStreamIdentificationFunction(
streamHandle,
streamIdentificationFunction,
outFacInputPortList,
inFacInputPortList,
inFacOutputPortList,
outFacOutputPortList);
}
void
EvbLan9668::AddStreamFilter(Ptr<StreamFilterInstance> streamFilterInstance)
{
NS_LOG_FUNCTION(this);
m_node->AddStreamFilter(streamFilterInstance);
}
uint16_t
EvbLan9668::AddFlowMeter(Ptr<FlowMeterInstance> flowMeterInstance)
{
NS_LOG_FUNCTION(this);
return m_node->AddFlowMeter(flowMeterInstance);
}
void
EvbLan9668::AddSequenceGenerationFunction(Ptr<SequenceGenerationFunction> entry)
{
NS_LOG_FUNCTION(this);
m_node->AddSequenceGenerationFunction(entry);
}
void
EvbLan9668::AddSequenceRecoveryFunction(Ptr<SequenceRecoveryFunction> rcvy, Ptr<BaseRecoveryFunction> algo, Ptr<LatentErrorDetectionFunction> lat, std::vector<uint32_t> port_ids)
{
NS_LOG_FUNCTION(this);
rcvy->SetAttribute("MinLatencyOverhead", TimeValue(m_minFrerRcvyLatencyOverhead));
rcvy->SetAttribute("MaxLatencyOverhead", TimeValue(m_maxFrerRcvyLatencyOverhead));
algo->SetAttribute("MinResetTimer", TimeValue(m_minFrerSeqRcvyResetDuration));
algo->SetAttribute("MaxResetTimer", TimeValue(m_maxFrerSeqRcvyResetDuration));
lat->SetAttribute("MinTestTimer", TimeValue(m_minFrerLatErrorTestDuration));
lat->SetAttribute("MaxTestTimer", TimeValue(m_maxFrerLatErrorTestDuration));
lat->SetAttribute("MinResetTimer", TimeValue(m_minFrerLatErrorResetDuration));
lat->SetAttribute("MaxResetTimer", TimeValue(m_maxFrerLatErrorResetDuration));
std::vector<Ptr<TsnNetDevice>> ports = {};
for (int i = 0; i < (int)port_ids.size(); i++){
ports.insert(ports.end(), GetPort(port_ids[i]));
}
rcvy->SetPorts(ports);
rcvy->SetRecoveryFunction(algo);
lat->SetRecoveryFunction(algo);
rcvy->SetLatentErrorDetectionFunction(lat);
m_node->AddSequenceRecoveryFunction(rcvy);
}
void
EvbLan9668::AddSequenceEncodeDecodeFunction(Ptr<SequenceEncodeDecodeFunction> entry, uint32_t port_id)
{
NS_LOG_FUNCTION(this);
entry->SetPort(GetPort(port_id));
m_node->AddSequenceEncodeDecodeFunction(entry);
}
}

View File

@@ -0,0 +1,220 @@
#ifndef EVB_LAN9668_H
#define EVB_LAN9668_H
#include "ns3/object.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/stream-identification-function-null.h"
namespace ns3
{
/**
* \ingroup real-device
*
* \brief A object to simulate EVB-LAN9668 switch.
*/
class EvbLan9668: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a EvbLan9668
*/
EvbLan9668();
/**
* \brief Create a EvbLan9668
*/
EvbLan9668(std::string name);
/**
* Destroy a EvbLan9668
*
* This is the destructor for the TsnNode.
*/
~EvbLan9668();
// Delete copy constructor and assignment operator to avoid misuse
EvbLan9668& operator=(const EvbLan9668&) = delete;
EvbLan9668(const EvbLan9668&) = delete;
/**
* \brief Set most of the hardware limits
*/
void SetHardwareLimits();
/**
* \brief Get a TsnNetDevice from its port id
* \param id the port id
* \return The TsnNetDevice
*/
Ptr<TsnNetDevice> GetPort(int id);
/**
* \brief Add a entry in the forwarding database
* \param dest the mac address destination
* \param vlan_id the vlan id
* \param output_port_ids a vector of output port id
*/
void AddForwardingTableEntry(
Mac48Address dest,
uint16_t vlan_id,
std::vector<uint32_t> output_port_ids);
/**
* \brief Set the datarate of a port
* \param id the port id
* \param d the datarate
*/
void SetPortDatarate(int id, DataRate d);
/**
* \brief Add a CBS instance to a port and queue
* \para the port id
* \param the queue id
* \param the iddle_slope
* \param the port_transmit_rate
*/
void AddCbs(uint32_t port_id, uint32_t queue_id, DataRate iddle_slope, DataRate port_transmit_rate);
/**
* \brief Add a GCL entry to a port
* \para the port id
* \param the GCL entry duration
* \param the GCL entry state
*/
void AddGclEntry(uint32_t port_id, Time interval, uint8_t state);
/**
* \brief Start TAS operation
*/
void StartTas();
/**
* \brief Add a null stream identification function
* \para streamHandle
* \param stream Identification Function
* \param out-facing input port ids
* \param in-facing input port ids
* \param in-facing output port ids
* \param out-facing output port ids
*/
void
AddNullStreamIdentificationFunction(
uint32_t streamHandle,
Ptr<NullStreamIdentificationFunction> streamIdentificationFunction,
std::vector<uint32_t> outFacInputPortIds,
std::vector<uint32_t> inFacInputPortIds,
std::vector<uint32_t> inFacOutputPortIds,
std::vector<uint32_t> outFacOutputPortIds);
/**
* \brief Add a stream filter to the node
* \param the stream filter instance
*/
void
AddStreamFilter(Ptr<StreamFilterInstance> streamFilterInstance);
/**
* \brief Add a flow meter to the node
* \param the flow meter instance
*/
uint16_t
AddFlowMeter(Ptr<FlowMeterInstance> flowMeterInstance);
/**
* \brief Add generation function to the node
* \param the genertation function
*/
void
AddSequenceGenerationFunction(Ptr<SequenceGenerationFunction> entry);
/**
* \brief Add recovery function to the node
* \param the recovery function
* \param the recovery algo (match or vector)
* \param the latent error detection function
* \param the port ids
*/
void
AddSequenceRecoveryFunction(Ptr<SequenceRecoveryFunction> rcvy, Ptr<BaseRecoveryFunction> algo, Ptr<LatentErrorDetectionFunction> lat, std::vector<uint32_t> port_ids);
/**
* \brief Add encode/decode function to the node
* \param the encode/decode function
* \param the port id
*/
void
AddSequenceEncodeDecodeFunction(Ptr<SequenceEncodeDecodeFunction> entry, uint32_t port_id);
protected:
private:
Ptr<TsnNode> m_node;
std::vector<Ptr<TsnNetDevice>> m_net_devices;
Ptr<SwitchNetDevice> m_switch_net_device;
//Hardware limits
uint16_t m_portNumber = 8;
uint16_t m_queuesPerPort = 8;
uint16_t m_maxFdbEntryNumber = 64;
QueueSize m_fifoSize = QueueSize("102p");
Time m_minForwardingLatency = NanoSeconds(2660); //2660
Time m_maxForwardingLatency = NanoSeconds(2370); //2370
//CBS
DataRate m_maxIddleSlope = DataRate("3.282Gb/s");
Time m_minCBSLatencyOverhead = NanoSeconds(0); //-25ns
Time m_maxCBSLatencyOverhead = NanoSeconds(0); //-9ns
//Tas
uint32_t m_maxGclEntryNumber = 256;
Time m_maxGclCycleDuration = NanoSeconds(999999999);
Time m_maxGclTimeInterval = NanoSeconds(999999999);
Time m_minTASLatencyOverhead = NanoSeconds(0);
Time m_maxTASLatencyOverhead = NanoSeconds(0);
//Stream identification
uint32_t m_maxSidEntryNumber = 127;
Time m_minNullSIDLatencyOverhead = NanoSeconds(0); //-9ns
Time m_maxNullSIDLatencyOverhead = NanoSeconds(0); //-13ns
Time m_minSourceMacSIDLatencyOverhead = NanoSeconds(0);
Time m_maxSourceMacSIDLatencyOverhead = NanoSeconds(0);
//PSFP
uint32_t m_maxPsfpFilterEntryNumber = 176;
uint32_t m_maxPsfpStreamGateEntryNumber = 176;
uint32_t m_maxPsfpFlowMeterEntryNumber = 240;
//FRER
uint32_t m_maxFrerSeqGenEntryNumber = 99;
uint32_t m_maxFrerSeqRcvyEntryNumber = 99;
uint32_t m_maxFrerSeqEncEntryNumber = 99;
Time m_minFrerSeqRcvyResetDuration = MilliSeconds(1);
Time m_maxFrerSeqRcvyResetDuration = Seconds(4.095);
Time m_minFrerLatErrorTestDuration = Seconds(1);
Time m_maxFrerLatErrorTestDuration = Seconds(86400);
Time m_minFrerLatErrorResetDuration = Seconds(1);
Time m_maxFrerLatErrorResetDuration = Seconds(86400);
Time m_minFrerRcvyLatencyOverhead = NanoSeconds(40); //40ns
Time m_maxFrerRcvyLatencyOverhead = NanoSeconds(40); //40ns
};
}
#endif /* EVB_LAN9668_H */

View File

@@ -0,0 +1,23 @@
#! /usr/bin/env python3
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("evb-lan9668-sp", "True", "True"),
("evb-lan9668-cbs", "True", "True"),
("evb-lan9668-tas", "True", "True"),
("evb-lan9668-psfp", "True", "True"),
("evb-lan9668-frer", "True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = []

View File

@@ -0,0 +1,86 @@
// Include a header file from your module to test.
#include "ns3/evb-lan9668.h"
// An essential include is test.h
#include "ns3/test.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;
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup real-device-tests Tests for real-device
* \ingroup real-device
* \ingroup tests
*/
// This is an example TestCase.
/**
* \ingroup real-device-tests
* Test case for feature 1
*/
class RealDeviceTestCase1 : public TestCase
{
public:
RealDeviceTestCase1();
virtual ~RealDeviceTestCase1();
private:
void DoRun() override;
};
// Add some help text to this case to describe what it is intended to test
RealDeviceTestCase1::RealDeviceTestCase1()
: TestCase("RealDevice test case (does nothing)")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
RealDeviceTestCase1::~RealDeviceTestCase1()
{
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
RealDeviceTestCase1::DoRun()
{
// A wide variety of test macros are available in src/core/test.h
NS_TEST_ASSERT_MSG_EQ(true, true, "true doesn't equal true for some reason");
// Use this one for floating point comparisons
NS_TEST_ASSERT_MSG_EQ_TOL(0.01, 0.01, 0.001, "Numbers are not equal within tolerance");
}
// 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 real-device-tests
* TestSuite for module real-device
*/
class RealDeviceTestSuite : public TestSuite
{
public:
RealDeviceTestSuite();
};
RealDeviceTestSuite::RealDeviceTestSuite()
: TestSuite("real-device", UNIT)
{
// TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
AddTestCase(new RealDeviceTestCase1, TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup real-device-tests
* Static variable for test initialization
*/
static RealDeviceTestSuite srealDeviceTestSuite;

View File

@@ -0,0 +1,24 @@
check_include_file_cxx(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
add_definitions(-DHAVE_STDINT_H)
endif()
set(examples_as_tests_sources)
if(${ENABLE_EXAMPLES})
set(examples_as_tests_sources
#test/trace-examples-test-suite.cc
)
endif()
build_lib(
LIBNAME trace
SOURCE_FILES model/animation-trace.cc
model/gantt-trace.cc
HEADER_FILES model/animation-trace.h
model/gantt-trace.h
LIBRARIES_TO_LINK ${libcore}
${libethernet}
${libtsn}
TEST_SOURCES test/trace-test-suite.cc
${examples_as_tests_sources}
)

View File

@@ -0,0 +1,19 @@
set(base_examples
tsn-point2point-netanim
tsn-switched-withFRER-netanim
)
foreach(
example
${base_examples}
)
build_lib_example(
NAME ${example}
SOURCE_FILES ${example}.cc
LIBRARIES_TO_LINK ${libtrace}
${libcore}
${libnetwork}
${libethernet}
${libtraffic-generator}
${libtsn}
)
endforeach()

View File

@@ -0,0 +1,102 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/animation-trace.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link. This example generate a NetAnim trace for graphical purpose.
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("AnimationTrace", LOG_LEVEL_ALL);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//NetAnim
// Create the animation trace object and configure for specified output
AnimationTrace anim = AnimationTrace("example-animation.xml");
anim.SetNodePosition(n1, 10, 0);
anim.StartAnimation();
Packet::EnablePrinting(); //Need to enable packet printing for metadata display on NetAnim
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,315 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#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"
#include "ns3/animation-trace.h"
/**
* \file
*
* Example with 2ES connected 4SW in a 1Gb/s full duplex link to demonstrate
* FRER usage. To be more specific this example 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.
*
* / ==== SW2 ==== \
* ESsource ==== SW1 SW4 ==== ESdest
* \ ==== SW3 ==== /
*
* This example generate a NetAnim trace for graphical purpose.
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create six nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("SW3", n4);
Ptr<TsnNode> n5 = CreateObject<TsnNode>();
Names::Add("SW4", n5);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Names::Add("ESsource#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ESdest#01", net1);
Ptr<TsnNetDevice> net2_1 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_1);
Ptr<TsnNetDevice> net2_2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_2);
Ptr<TsnNetDevice> net2_3 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_3);
Ptr<TsnNetDevice> net3_1 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_1);
Ptr<TsnNetDevice> net3_2 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_2);
Ptr<TsnNetDevice> net4_1 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_1);
Ptr<TsnNetDevice> net4_2 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_2);
Ptr<TsnNetDevice> net5_1 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_1);
Ptr<TsnNetDevice> net5_2 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_2);
Ptr<TsnNetDevice> net5_3 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_3);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
net0->Attach(l0);
net2_1->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
net2_2->Attach(l1);
net3_1->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
net2_3->Attach(l2);
net4_1->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
net3_2->Attach(l3);
net5_1->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
net4_2->Attach(l4);
net5_2->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
net5_3->Attach(l5);
net1->Attach(l5);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
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<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(net3_1);
sw2->AddSwitchPort(net3_2);
Ptr<SwitchNetDevice> sw3 = CreateObject<SwitchNetDevice>();
sw3->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw3->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n4->AddDevice(sw3);
sw3->AddSwitchPort(net4_1);
sw3->AddSwitchPort(net4_2);
Ptr<SwitchNetDevice> sw4 = CreateObject<SwitchNetDevice>();
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<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//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<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({StreamHandle});
n2->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
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<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
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<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
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<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(10)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
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(41));
//Callback to display the packet transmitted and received log
//Packet::EnablePrinting();
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the packet elimination on last output port
net5_3->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(net5_3)));
//NetAnim
// Create the animation trace object and configure for specified output
AnimationTrace anim = AnimationTrace("example-animation.xml");
anim.SetNodePosition(n0, 0, 0);
anim.SetNodePosition(n1, 40, 0);
anim.SetNodePosition(n2, 10, 0);
anim.SetNodePosition(n3, 20, -10);
anim.SetNodePosition(n4, 20, 10);
anim.SetNodePosition(n5, 30, 0);
anim.StartAnimation();
Packet::EnablePrinting(); //Need to enable packet printing for metadata display on NetAnim
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,500 @@
#include "animation-trace.h"
#include "ns3/core-module.h"
#include "ns3/config.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/net-device.h"
#include "ns3/channel.h"
#include "ns3/node-container.h"
#include "ns3/node-list.h"
#include "ns3/node.h"
#include "ns3/nstime.h"
#include "ns3/ptr.h"
#include "ns3/simulator.h"
#include <cstdio>
#include <string>
#include <fstream>
#include <iomanip>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("AnimationTrace");
NS_OBJECT_ENSURE_REGISTERED(AnimationTrace);
AnimationTrace::AnimationTrace(std::string filename)
{
NS_LOG_FUNCTION(this);
m_outputFileName = filename;
}
AnimationTrace::~AnimationTrace()
{
NS_LOG_FUNCTION(this);
StopAnimation();
}
void
AnimationTrace::StartAnimation()
{
NS_LOG_FUNCTION(this);
m_currentPktCount = 0;
SetOutputFile(m_outputFileName);
WriteXmlAnim();
WriteNodes();
WriteNodeColors();
WriteNodeSizes();
WriteNodeDescription();
WriteLinkProperties();
ConnectCallbacks();
}
void
AnimationTrace::StopAnimation()
{
NS_LOG_INFO("Stopping Animation");
if (m_f)
{
// Terminate the anim element
WriteXmlClose("anim");
std::fclose(m_f);
m_f = nullptr;
}
}
void
AnimationTrace::SetNodePosition(Ptr<Node> n, int x, int y)
{
NS_LOG_FUNCTION(this);
m_nodeLocation[n->GetId()] = {x,y};
}
void
AnimationTrace::SetOutputFile(const std::string& fn)
{
NS_LOG_FUNCTION(this);
FILE* f = nullptr;
f = std::fopen(fn.c_str(), "w");
if (!f)
{
NS_FATAL_ERROR("Unable to open output file:" << fn);
return; // Can't open output file
}
m_f = f;
}
void
AnimationTrace::WriteXmlAnim()
{
NS_LOG_FUNCTION(this);
AnimXmlElement element("anim");
element.AddAttribute("ver", m_version);
FILE* f = m_f;
element.AddAttribute("filetype", "animation");
WriteN(element.ToString(false) + ">\n", f);
}
void
AnimationTrace::WriteXmlClose(std::string name)
{
std::string closeString = "</" + name + ">\n";
WriteN(closeString, m_f);
}
void
AnimationTrace::WriteNodes()
{
for (auto i = NodeList::Begin(); i != NodeList::End(); ++i)
{
Ptr<Node> n = *i;
NS_LOG_INFO("Update Position for Node: " << n->GetId());
std::vector<int> pos = {0,0};
if (m_nodeLocation.count(n->GetId()) > 0)
{
pos = m_nodeLocation[n->GetId()];
}
WriteXmlNode(n->GetId(), n->GetSystemId(), pos[0], pos[1]);
}
}
void
AnimationTrace::WriteXmlNode(uint32_t id, uint32_t sysId, double locX, double locY)
{
NS_LOG_FUNCTION(this);
AnimXmlElement element("node");
element.AddAttribute("id", id);
element.AddAttribute("sysId", sysId);
element.AddAttribute("locX", locX);
element.AddAttribute("locY", locY);
WriteN(element.ToString(), m_f);
}
void
AnimationTrace::WriteNodeColors()
{
NS_LOG_FUNCTION(this);
for (auto i = NodeList::Begin(); i != NodeList::End(); ++i)
{
Ptr<Node> n = *i;
WriteXmlNodeColors(n->GetId(), 255, 0, 0);
}
}
void
AnimationTrace::WriteXmlNodeColors(uint32_t nodeId, uint8_t r, uint8_t g, uint8_t b)
{
NS_ASSERT(NodeList::GetNode(nodeId));
NS_LOG_INFO("Setting node color for Node Id:" << nodeId);
AnimXmlElement element("nu");
element.AddAttribute("p", "c");
element.AddAttribute("t", Simulator::Now().GetSeconds());
element.AddAttribute("id", nodeId);
element.AddAttribute("r", (uint32_t)r);
element.AddAttribute("g", (uint32_t)g);
element.AddAttribute("b", (uint32_t)b);
WriteN(element.ToString(), m_f);
}
void
AnimationTrace::WriteNodeSizes()
{
for (auto i = NodeList::Begin(); i != NodeList::End(); ++i)
{
Ptr<Node> n = *i;
NS_LOG_INFO("Update Size for Node: " << n->GetId());
WriteXmlUpdateNodeSize(n->GetId(), .5, .5);
}
}
void
AnimationTrace::WriteXmlUpdateNodeSize(uint32_t nodeId, double width, double height)
{
AnimXmlElement element("nu");
element.AddAttribute("p", "s");
element.AddAttribute("t", Simulator::Now().GetSeconds());
element.AddAttribute("id", nodeId);
element.AddAttribute("w", width);
element.AddAttribute("h", height);
WriteN(element.ToString(), m_f);
}
void
AnimationTrace::WriteNodeDescription()
{
for (auto i = NodeList::Begin(); i != NodeList::End(); ++i)
{
Ptr<Node> n = *i;
NS_LOG_INFO("Update Description for Node: " << n->GetId());
WriteXmlUpdateNodeDescription(n);
}
}
void
AnimationTrace::WriteXmlUpdateNodeDescription(Ptr<Node> n)
{
AnimXmlElement element("nu");
element.AddAttribute("p", "d");
element.AddAttribute("t", Simulator::Now().GetSeconds());
element.AddAttribute("id", n->GetId());
std::string name = Names::FindName(n);
if (name != "")
{
element.AddAttribute("descr", name, true);
WriteN(element.ToString(), m_f);
}
}
void
AnimationTrace::WriteLinkProperties()
{
for (auto i = NodeList::Begin(); i != NodeList::End(); ++i)
{
Ptr<Node> n = *i;
uint32_t n1Id = n->GetId();
uint32_t nDev = n->GetNDevices(); // Number of devices
for (uint32_t i = 0; i < nDev; ++i)
{
Ptr<NetDevice> dev = n->GetDevice(i);
NS_ASSERT(dev);
Ptr<Channel> ch = dev->GetChannel();
std::string channelType = "Unknown channel";
if (ch)
{
channelType = ch->GetInstanceTypeId().GetName();
}
NS_LOG_DEBUG("Got ChannelType" << channelType);
if (channelType == "ns3::EthernetChannel")
{ // Since these are duplex links, we only need to dump
// if srcid < dstid
std::size_t nChDev = ch->GetNDevices();
for (std::size_t j = 0; j < nChDev; ++j)
{
Ptr<NetDevice> chDev = ch->GetDevice(j);
uint32_t n2Id = chDev->GetNode()->GetId();
if (n1Id < n2Id)
{
WriteXmlLink(n1Id, 0, n2Id);
}
}
}
else
{
NS_LOG_INFO("Unknown channel type : " << channelType);
}
}
}
}
void
AnimationTrace::WriteXmlLink(uint32_t fromId, uint32_t toLp, uint32_t toId)
{
AnimXmlElement element("link");
element.AddAttribute("fromId", fromId);
element.AddAttribute("toId", toId);
element.AddAttribute("fd", "", true); ///< from node description
element.AddAttribute("td", "", true); ///< to node description
element.AddAttribute("ld", "", true); ///< link description
WriteN(element.ToString(), m_f);
}
void
AnimationTrace::WriteXmlP(std::string pktType,
uint32_t fId,
double fbTx,
double lbTx,
uint32_t tId,
double fbRx,
double lbRx,
std::string metaInfo)
{
AnimXmlElement element(pktType);
element.AddAttribute("fId", fId);
element.AddAttribute("fbTx", fbTx);
element.AddAttribute("lbTx", lbTx);
if (!metaInfo.empty())
{
element.AddAttribute("meta-info", metaInfo.c_str(), true);
}
element.AddAttribute("tId", tId);
element.AddAttribute("fbRx", fbRx);
element.AddAttribute("lbRx", lbRx);
WriteN(element.ToString(), m_f);
}
void
AnimationTrace::ConnectCallbacks()
{
// Connect the callbacks
Config::ConnectFailSafe("/ChannelList/*/TxRxEthernet",
MakeCallback(&AnimationTrace::DevTxTrace, this));
}
void
AnimationTrace::DevTxTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> tx,
Ptr<NetDevice> rx,
Time txTime,
Time rxTime)
{
NS_LOG_FUNCTION(this);
NS_ASSERT(tx);
NS_ASSERT(rx);
Time now = Simulator::Now();
double fbTx = now.GetSeconds();
double lbTx = (now + txTime).GetSeconds();
double fbRx = (now + rxTime - txTime).GetSeconds();
double lbRx = (now + rxTime).GetSeconds();
if(CheckMaxPktsPerTraceFile())
{
WriteXmlP("p",
tx->GetNode()->GetId(),
fbTx,
lbTx,
rx->GetNode()->GetId(),
fbRx,
lbRx,
GetPacketMetadata(p));
}
}
bool
AnimationTrace::CheckMaxPktsPerTraceFile()
{
++m_currentPktCount;
if (m_currentPktCount <= m_maxPkts)
{
return true;
}
NS_LOG_INFO("Max Packets exceeded !");
return false;
}
int
AnimationTrace::WriteN(const std::string& st, FILE* f)
{
if (!f)
{
return 0;
}
return WriteN(st.c_str(), st.length(), f);
}
int
AnimationTrace::WriteN(const char* data, uint32_t count, FILE* f)
{
if (!f)
{
return 0;
}
// Write count bytes to h from data
uint32_t nLeft = count;
const char* p = data;
uint32_t written = 0;
while (nLeft)
{
int n = std::fwrite(p, 1, nLeft, f);
if (n <= 0)
{
return written;
}
written += n;
nLeft -= n;
p += n;
}
return written;
}
std::string
AnimationTrace::GetPacketMetadata(Ptr<const Packet> p)
{
NS_LOG_FUNCTION(this);
std::ostringstream oss;
p->Print(oss);
return oss.str();
}
/***** AnimXmlElement *****/
AnimationTrace::AnimXmlElement::AnimXmlElement(std::string tagName, bool emptyElement)
: m_tagName(tagName),
m_text("")
{
}
template <typename T>
void
AnimationTrace::AnimXmlElement::AddAttribute(std::string attribute, T value, bool xmlEscape)
{
std::ostringstream oss;
oss << std::setprecision(10);
oss << value;
std::string attributeString = attribute;
if (xmlEscape)
{
attributeString += "=\"";
std::string valueStr = oss.str();
for (auto it = valueStr.begin(); it != valueStr.end(); ++it)
{
switch (*it)
{
case '&':
attributeString += "&amp;";
break;
case '\"':
attributeString += "&quot;";
break;
case '\'':
attributeString += "&apos;";
break;
case '<':
attributeString += "&lt;";
break;
case '>':
attributeString += "&gt;";
break;
default:
attributeString += *it;
break;
}
}
attributeString += "\" ";
}
else
{
attributeString += "=\"" + oss.str() + "\" ";
}
m_attributes.push_back(attributeString);
}
void
AnimationTrace::AnimXmlElement::AppendChild(AnimXmlElement e)
{
m_children.push_back(e.ToString());
}
void
AnimationTrace::AnimXmlElement::SetText(std::string text)
{
m_text = text;
}
std::string
AnimationTrace::AnimXmlElement::ToString(bool autoClose)
{
std::string elementString = "<" + m_tagName + " ";
for (auto i = m_attributes.begin(); i != m_attributes.end(); ++i)
{
elementString += *i;
}
if (m_children.empty() && m_text.empty())
{
if (autoClose)
{
elementString += "/>";
}
}
else
{
elementString += ">";
if (!m_text.empty())
{
elementString += m_text;
}
if (!m_children.empty())
{
elementString += "\n";
for (auto i = m_children.begin(); i != m_children.end(); ++i)
{
elementString += *i + "\n";
}
}
if (autoClose)
{
elementString += "</" + m_tagName + ">";
}
}
return elementString + ((autoClose) ? "\n" : "");
}
}

View File

@@ -0,0 +1,142 @@
#ifndef ANIMATION_TRACE_H
#define ANIMATION_TRACE_H
#include "ns3/object.h"
#include "ns3/config.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/node-list.h"
#include "ns3/nstime.h"
#include "ns3/ptr.h"
#include "ns3/simulator.h"
#include <cstdio>
#include <string>
// Add a doxygen group for this module.
// If you have more than one file, this should be in only one of them.
/**
* \defgroup animation-trace Description of the animation-trace
*/
namespace ns3
{
class AnimationTrace : public Object
{
public:
/**
* \brief Constructor
* \param filename The Filename for the trace file used by the Animator
*
*/
AnimationTrace(const std::string filename);
/**
* \brief Destructor for the animator interface.
*
*/
~AnimationTrace();
void StartAnimation();
void StopAnimation();
void SetNodePosition(Ptr<Node> n, int x, int y);
/// AnimXmlElement class
class AnimXmlElement
{
public:
/**
* Constructor
*
* \param tagName tag name
* \param emptyElement empty element?
*/
AnimXmlElement(std::string tagName, bool emptyElement = true);
template <typename T>
/**
* Add attribute function
* \param attribute the attribute name
* \param value the attribute value
* \param xmlEscape true to escape
*/
void AddAttribute(std::string attribute, T value, bool xmlEscape = false);
/**
* Set text function
* \param text the text for the element
*/
void SetText(std::string text);
/**
* Append child function
* \param e the element to add as a child
*/
void AppendChild(AnimXmlElement e);
/**
* Get text for the element function
* \param autoClose auto close the element
* \returns the text
*/
std::string ToString(bool autoClose = true);
private:
std::string m_tagName; ///< tag name
std::string m_text; ///< element string
std::vector<std::string> m_attributes; ///< list of attributes
std::vector<std::string> m_children; ///< list of children
};
private:
void SetOutputFile(const std::string& fn);
void WriteNodes();
void WriteNodeColors();
void WriteNodeSizes();
void WriteNodeDescription();
void WriteLinkProperties();
void WriteXmlAnim();
void WriteXmlClose(std::string name);
void WriteXmlNode(uint32_t id, uint32_t sysId, double locX, double locY);
void WriteXmlNodeColors(uint32_t nodeId, uint8_t r, uint8_t g, uint8_t b);
void WriteXmlUpdateNodeSize(uint32_t nodeId, double width, double height);
void WriteXmlUpdateNodeDescription(Ptr<Node> n);
void WriteXmlLink(uint32_t fromId, uint32_t toLp, uint32_t toId);
void WriteXmlP(std::string pktType,
uint32_t fId,
double fbTx,
double lbTx,
uint32_t tId,
double fbRx,
double lbRx,
std::string metaInfo = "");
void ConnectCallbacks();
void DevTxTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> tx,
Ptr<NetDevice> rx,
Time txTime,
Time rxTime);
bool CheckMaxPktsPerTraceFile();
int WriteN(const std::string& st, FILE* f);
int WriteN(const char* data, uint32_t count, FILE* f);
std::string GetPacketMetadata(Ptr<const Packet> p);
std::string m_version = "netanim-3.109";
std::string m_outputFileName;
uint64_t m_currentPktCount = 0;
uint64_t m_maxPkts = 10000;
FILE* m_f;
uint64_t gAnimUid; ///< Packet unique identifier used by AnimationInterface
std::map<uint32_t, std::vector<int>> m_nodeLocation; ///< node location
std::map<uint32_t, std::string> m_nodeDescription;
};
}
#endif /* ANIMATION_TRACE_H */

View File

@@ -0,0 +1,231 @@
#include "gantt-trace.h"
#include "ns3/core-module.h"
#include "ns3/config.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/net-device.h"
#include "ns3/channel.h"
#include "ns3/node-container.h"
#include "ns3/node-list.h"
#include "ns3/node.h"
#include "ns3/nstime.h"
#include "ns3/ptr.h"
#include "ns3/simulator.h"
#include <cstdio>
#include <string>
#include <fstream>
#include <iomanip>
#include "ns3/ethernet-header2.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("GanttTrace");
NS_OBJECT_ENSURE_REGISTERED(GanttTrace);
GanttTrace::GanttTrace(std::string filename)
{
NS_LOG_FUNCTION(this);
m_outputFileName = filename;
m_f = nullptr;
}
GanttTrace::~GanttTrace()
{
NS_LOG_FUNCTION(this);
StopGantt();
}
void
GanttTrace::StartGantt()
{
NS_LOG_FUNCTION(this);
SetOutputFile(m_outputFileName);
ConnectCallbacks();
}
void
GanttTrace::StopGantt()
{
NS_LOG_INFO("Stopping Gantt");
if (m_f)
{
// Terminate the anim element
std::fclose(m_f);
m_f = nullptr;
}
}
void
GanttTrace::SetOutputFile(const std::string& fn)
{
NS_LOG_FUNCTION(this);
FILE* f = nullptr;
f = std::fopen(fn.c_str(), "w");
if (!f)
{
NS_FATAL_ERROR("Unable to open output file:" << fn);
return; // Can't open output file
}
m_f = f;
NS_ASSERT(m_f);
}
void
GanttTrace::ConnectCallbacks()
{
// Connect the callbacks
Config::ConnectFailSafe("/ChannelList/*/TxRxEthernet",
MakeCallback(&GanttTrace::DevTxTrace, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/MacTxAnimation",
MakeCallback(&GanttTrace::FifoEntryTrace, this));
}
void
GanttTrace::DevTxTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> tx,
Ptr<NetDevice> rx,
Time txTime,
Time rxTime)
{
NS_LOG_FUNCTION(this);
NS_ASSERT(tx);
NS_ASSERT(rx);
Time fbTx = Simulator::Now(); //Firt bit Tx time
Time lbTx = fbTx + txTime; //Last bit Tx time
Time fbRx = fbTx + rxTime - txTime; //First bit Rx time
Time lbRx = fbTx + rxTime; //Last bit Rx time
Ptr<Packet> copyPacket = p->Copy();
EthernetHeader2 ethHeader;
copyPacket->RemoveHeader(ethHeader);
// NS_LOG_INFO("CB TX triggered !");
// NS_LOG_INFO(" SimTime : " << Simulator::Now().GetNanoSeconds() << "ns");
// NS_LOG_INFO(" From NetDevice : " << Names::FindName(tx));
// NS_LOG_INFO(" To NetDevice : " << Names::FindName(rx));
// NS_LOG_INFO(" tx time : " << fbTx.GetNanoSeconds());
// NS_LOG_INFO(" rx time : " << lbRx.GetNanoSeconds());
// NS_LOG_INFO(" Pkt id : " << int(p->GetUid()));
// NS_LOG_INFO(" Pkt vlanId: " << ethHeader.GetVid());
// NS_LOG_INFO(" Pkt prio : " << int(ethHeader.GetPcp()));
WritePacketTxEvent(Names::FindName(tx), Names::FindName(rx), fbTx, lbTx, fbRx, lbRx, p->GetUid(), ethHeader.GetVid(), ethHeader.GetPcp());
}
void
GanttTrace::FifoEntryTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> net)
{
NS_LOG_FUNCTION(this);
// NS_LOG_INFO("CB : Pkt enter a FIFO !");
// NS_LOG_INFO(" SimTime : " << Simulator::Now().GetNanoSeconds() << "ns");
// NS_LOG_INFO(" On NetDevice : " << Names::FindName(net));
// NS_LOG_INFO(" Pkt id : " << int(p->GetUid()));
Ptr<Packet> copyPacket = p->Copy();
EthernetHeader2 ethHeader;
copyPacket->RemoveHeader(ethHeader);
WriteFifoEnterEvent(Simulator::Now(), Names::FindName(net), int(p->GetUid()), ethHeader.GetPcp());
}
void
GanttTrace::WritePacketTxEvent(std::string srcNetDeviceName,
std::string dstNetDeviceName,
Time txBeginTime,
Time txEndTime,
Time rxBeginTime,
Time rxEndTime,
int pktId,
int vlanId,
int prio)
{
NS_LOG_FUNCTION(this);
WriteN("TxEvent, pktId=" + std::to_string(pktId) +
",src=" + srcNetDeviceName +
", dst=" + dstNetDeviceName +
", txBegin=" + std::to_string(txBeginTime.GetNanoSeconds()) +
", txEnd=" + std::to_string(txEndTime.GetNanoSeconds()) +
", rxBegin=" + std::to_string(rxBeginTime.GetNanoSeconds()) +
", rxEnd=" + std::to_string(rxEndTime.GetNanoSeconds()) +
", vlanId=" + std::to_string(vlanId) +
", prio=" + std::to_string(prio) +
"\n", m_f);
}
void
GanttTrace::WriteFifoEnterEvent(Time t, std::string netDeviceName, int pktId, int fifoId)
{
NS_LOG_FUNCTION(this);
WriteN("FifoEnterEvent, pktId=" + std::to_string(pktId) +
", src=" + netDeviceName +
", t=" + std::to_string(t.GetNanoSeconds()) +
", fifoId=" + std::to_string(fifoId) +
"\n", m_f);
}
int
GanttTrace::WriteN(const std::string& st, FILE* f)
{
if (!f)
{
return 0;
}
return WriteN(st.c_str(), st.length(), f);
}
int
GanttTrace::WriteN(const char* data, uint32_t count, FILE* f)
{
if (!f)
{
return 0;
}
// Write count bytes to h from data
uint32_t nLeft = count;
const char* p = data;
uint32_t written = 0;
while (nLeft)
{
int n = std::fwrite(p, 1, nLeft, f);
if (n <= 0)
{
return written;
}
written += n;
nLeft -= n;
p += n;
}
return written;
}
std::string
GanttTrace::GetPacketMetadata(Ptr<const Packet> p)
{
NS_LOG_FUNCTION(this);
std::ostringstream oss;
p->Print(oss);
return oss.str();
}
}

View File

@@ -0,0 +1,86 @@
#ifndef GANTT_TRACE_H
#define GANTT_TRACE_H
#include "ns3/object.h"
#include "ns3/config.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/node-list.h"
#include "ns3/nstime.h"
#include "ns3/ptr.h"
#include "ns3/simulator.h"
#include <cstdio>
#include <string>
// Add a doxygen group for this module.
// If you have more than one file, this should be in only one of them.
/**
* \defgroup gantt-trace Description of the gantt-trace
*/
namespace ns3
{
class GanttTrace : public Object
{
public:
/**
* \brief Constructor
* \param filename The Filename for the trace file used by the Animator
*
*/
GanttTrace(const std::string filename);
/**
* \brief Destructor for the animator interface.
*
*/
~GanttTrace();
void StartGantt();
void StopGantt();
private:
void SetOutputFile(const std::string& fn);
void ConnectCallbacks();
void DevTxTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> tx,
Ptr<NetDevice> rx,
Time txTime,
Time rxTime);
void FifoEntryTrace(std::string context,
Ptr<const Packet> p,
Ptr<NetDevice> net);
void WritePacketTxEvent(std::string srcNetDeviceName,
std::string dstNetDeviceName,
Time txBeginTime,
Time txEndTime,
Time rxBeginTime,
Time rxEndTime,
int pktId,
int vlanId,
int prio);
void WriteFifoEnterEvent(Time t,
std::string netDeviceName,
int pktId,
int fifoId);
int WriteN(const std::string& st, FILE* f);
int WriteN(const char* data, uint32_t count, FILE* f);
std::string GetPacketMetadata(Ptr<const Packet> p);
std::string m_outputFileName;
FILE* m_f;
};
}
#endif /* GANTT_TRACE_H */

View File

@@ -0,0 +1,20 @@
#! /usr/bin/env python3
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("tsn-point2point-netanim", "True", "True"),
("tsn-switched-withFRER-netanim", "True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = []

View File

@@ -0,0 +1,86 @@
// Include a header file from your module to test.
#include "ns3/animation-trace.h"
// An essential include is test.h
#include "ns3/test.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;
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup animation-trace-tests Tests for animation-trace
* \ingroup animation-trace
* \ingroup tests
*/
// This is an example TestCase.
/**
* \ingroup animation-trace-tests
* Test case for feature 1
*/
class AnimationTraceTestCase1 : public TestCase
{
public:
AnimationTraceTestCase1();
virtual ~AnimationTraceTestCase1();
private:
void DoRun() override;
};
// Add some help text to this case to describe what it is intended to test
AnimationTraceTestCase1::AnimationTraceTestCase1()
: TestCase("AnimationTrace test case (does nothing)")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
AnimationTraceTestCase1::~AnimationTraceTestCase1()
{
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
AnimationTraceTestCase1::DoRun()
{
// A wide variety of test macros are available in src/core/test.h
NS_TEST_ASSERT_MSG_EQ(true, true, "true doesn't equal true for some reason");
// Use this one for floating point comparisons
NS_TEST_ASSERT_MSG_EQ_TOL(0.01, 0.01, 0.001, "Numbers are not equal within tolerance");
}
// 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 animation-trace-tests
* TestSuite for module animation-trace
*/
class AnimationTraceTestSuite : public TestSuite
{
public:
AnimationTraceTestSuite();
};
AnimationTraceTestSuite::AnimationTraceTestSuite()
: TestSuite("animation-trace", UNIT)
{
// TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
AddTestCase(new AnimationTraceTestCase1, TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup animation-trace-tests
* Static variable for test initialization
*/
static AnimationTraceTestSuite sanimationTraceTestSuite;

View File

@@ -0,0 +1,27 @@
check_include_file_cxx(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
add_definitions(-DHAVE_STDINT_H)
endif()
set(examples_as_tests_sources)
if(${ENABLE_EXAMPLES})
set(examples_as_tests_sources
#test/traffic-generator-examples-test-suite.cc
)
endif()
build_lib(
LIBNAME traffic-generator
SOURCE_FILES model/traffic-generator.cc
model/ethernet-generator.cc
model/myCustomApp.cc
helper/traffic-generator-helper.cc
HEADER_FILES model/traffic-generator.h
model/ethernet-generator.h
model/myCustomApp.h
helper/traffic-generator-helper.h
LIBRARIES_TO_LINK ${libcore}
${libethernet}
TEST_SOURCES test/traffic-generator-test-suite.cc
${examples_as_tests_sources}
)

View File

@@ -0,0 +1,7 @@
build_lib_example(
NAME traffic-generator-ethernet
SOURCE_FILES traffic-generator-ethernet.cc
LIBRARIES_TO_LINK ${libcore}
${libnetwork}
${libethernet}
)

View File

@@ -0,0 +1,82 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of ethernet-generator.cc on a network composed of two
* ethernet end-stations
* ES1 ====== ES2
*/
using namespace ns3;
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<Node> n0 = CreateObject<Node>();
Names::Add("ES1", n0);
Ptr<Node> n1 = CreateObject<Node>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<EthernetNetDevice> net0 = CreateObject<EthernetNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<EthernetNetDevice> net1 = CreateObject<EthernetNetDevice>();
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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 two 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 description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net1->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("InterFrame", TimeValue(MilliSeconds(200)));
app0->SetAttribute("Offset", TimeValue(MilliSeconds(100)));
app0->SetAttribute("VlanID", UintegerValue(100));
app0->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,8 @@
#include "traffic-generator-helper.h"
namespace ns3
{
/* ... */
}

View File

@@ -0,0 +1,16 @@
#ifndef TRAFFIC_GENERATOR_HELPER_H
#define TRAFFIC_GENERATOR_HELPER_H
#include "ns3/traffic-generator.h"
namespace ns3
{
// Each class should be documented using Doxygen,
// and have an \ingroup traffic-generator directive
/* ... */
}
#endif /* TRAFFIC_GENERATOR_HELPER_H */

View File

@@ -0,0 +1,153 @@
#include "ethernet-generator.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/socket-factory.h"
#include "ns3/socket.h"
#include "ns3/uinteger.h"
#include "ns3/names.h"
#include "ns3/ethernet-net-device.h"
#include <cstdio>
#include <cstdlib>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EthernetGenerator");
NS_OBJECT_ENSURE_REGISTERED(EthernetGenerator);
TypeId
EthernetGenerator::GetTypeId()
{
static TypeId tid =
TypeId("ns3::EthernetGenerator")
.SetParent<Application>()
.SetGroupName("Applications")
.AddConstructor<EthernetGenerator>()
.AddAttribute("Address",
"Destination Mac Address",
AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")),
MakeAddressAccessor(&EthernetGenerator::m_destAddress),
MakeAddressChecker())
.AddAttribute("PayloadSize",
"Payload Size in bytes",
UintegerValue(100),
MakeUintegerAccessor(&EthernetGenerator::m_payload_size),
MakeUintegerChecker<uint16_t>())
.AddAttribute("BurstSize",
"Burst size",
UintegerValue(1),
MakeUintegerAccessor(&EthernetGenerator::m_burst_size),
MakeUintegerChecker<uint16_t>())
.AddAttribute("Period",
"Period between burst",
TimeValue(Seconds(1)),
MakeTimeAccessor(&EthernetGenerator::m_period),
MakeTimeChecker())
.AddAttribute("InterFrame",
"Period between two packet of a burst",
TimeValue(Seconds(0)),
MakeTimeAccessor(&EthernetGenerator::m_interframe),
MakeTimeChecker())
.AddAttribute("Offset",
"Time offset between application start and first packet emission",
TimeValue(Seconds(0)),
MakeTimeAccessor(&EthernetGenerator::m_offset),
MakeTimeChecker())
.AddAttribute("VlanID",
"Vlan ID",
UintegerValue(65535),
MakeUintegerAccessor(&EthernetGenerator::m_vid),
MakeUintegerChecker<uint16_t>())
.AddAttribute("PCP",
"PCP field",
UintegerValue(0),
MakeUintegerAccessor(&EthernetGenerator::m_pcp),
MakeUintegerChecker<uint8_t>())
.AddAttribute("DEI",
"DEI bit",
UintegerValue(0),
MakeUintegerAccessor(&EthernetGenerator::m_dei),
MakeUintegerChecker<uint8_t>())
.AddTraceSource("PktSent",
"Trace source indicating a packet was given to the netDevice"
"by the application",
MakeTraceSourceAccessor(&EthernetGenerator::m_pktSentTrace),
"ns3::EthernetGenerator::PacketVlanTraceCallback");
return tid;
}
EthernetGenerator::EthernetGenerator()
{
NS_LOG_FUNCTION(this);
m_net = nullptr;
}
EthernetGenerator::~EthernetGenerator()
{
NS_LOG_FUNCTION(this);
}
void
EthernetGenerator::Setup(Ptr<EthernetNetDevice> net)
{
m_net = net;
}
void
EthernetGenerator::DoDispose()
{
NS_LOG_FUNCTION(this);
Application::DoDispose();
}
void
EthernetGenerator::StartApplication()
{
NS_LOG_FUNCTION(this);
if (m_vid==65535 && m_payload_size < MIN_PAYLOAD_SIZE + 4){
m_payload_size = MIN_PAYLOAD_SIZE + 4;
}
else if(m_payload_size < MIN_PAYLOAD_SIZE){
m_payload_size = MIN_PAYLOAD_SIZE;
}
m_sendEvent = Simulator::Schedule(m_offset, &EthernetGenerator::SendBurst, this);
}
void
EthernetGenerator::StopApplication()
{
NS_LOG_FUNCTION(this);
Simulator::Cancel(m_sendEvent);
}
void
EthernetGenerator::SendBurst()
{
NS_LOG_FUNCTION(this);
for (int i = 0; i < m_burst_size; i++) {
Simulator::Schedule(m_interframe*i, &EthernetGenerator::Send, this);
}
m_sendEvent = Simulator::Schedule(m_period, &EthernetGenerator::SendBurst, this);
}
void
EthernetGenerator::Send()
{
NS_LOG_FUNCTION(this);
Ptr<Packet> p = Create<Packet>(m_payload_size);
//NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << Names::FindName(m_net->GetNode()) << "/" << Names::FindName(m_net) <<" : Pkt #" << p->GetUid() <<"(vid="<< m_vid <<") given to the netDevice ! " << p->ToString());
if (m_vid==65535){
m_net->Send(p, m_destAddress, 0xEDE1);
}
else{
m_net->Send(p, m_destAddress, 0xEDE1, m_vid, m_pcp, m_dei);
}
m_pktSentTrace(p, m_vid);
}
} // Namespace ns3

View File

@@ -0,0 +1,72 @@
#ifndef ETHERNET_GENERATOR_H
#define ETHERNET_GENERATOR_H
#include "ns3/application.h"
#include "ns3/event-id.h"
#include "ns3/ptr.h"
#include <ns3/traced-callback.h>
#include "ns3/ethernet-net-device.h"
namespace ns3
{
class Socket;
class Packet;
class EthernetGenerator : public Application
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId();
EthernetGenerator();
~EthernetGenerator() override;
/**
* \return the total bytes sent by this app
*/
void Setup(Ptr<EthernetNetDevice> net);
typedef TracedCallback<Ptr<const Packet>, uint16_t> PacketVlanTraceCallback;
protected:
void DoDispose() override;
private:
void StartApplication() override;
void StopApplication() override;
/**
* \brief Send a packet
*/
void SendBurst();
void Send();
Address m_destAddress;
int m_payload_size;
int m_burst_size;
Time m_period;
Time m_interframe;
Time m_offset;
uint16_t m_vid;
uint8_t m_pcp;
uint8_t m_dei;
EventId m_sendEvent; //!< Event to send the next packet
Ptr<EthernetNetDevice> m_net;
PacketVlanTraceCallback m_pktSentTrace;
static const uint16_t MIN_PAYLOAD_SIZE = 42; //Min payload size with VLAN
};
} // namespace ns3
#endif

View File

@@ -0,0 +1,119 @@
#include "myCustomApp.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/socket-factory.h"
#include "ns3/socket.h"
#include "ns3/uinteger.h"
#include "ns3/names.h"
#include "ns3/point-to-point-net-device.h"
#include <cstdio>
#include <cstdlib>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("myCustomApp");
NS_OBJECT_ENSURE_REGISTERED(myCustomApp);
TypeId
myCustomApp::GetTypeId()
{
static TypeId tid =
TypeId("ns3::myCustomApp")
.SetParent<Application>()
.SetGroupName("Applications")
.AddConstructor<myCustomApp>();
return tid;
}
myCustomApp::myCustomApp()
{
NS_LOG_FUNCTION(this);
m_sent = 0;
m_totalTx = 0;
m_totalRx = 0;
m_sendEvent = EventId();
}
myCustomApp::~myCustomApp()
{
NS_LOG_FUNCTION(this);
}
void
myCustomApp::Setup(Ptr<PointToPointNetDevice> net, int t)
{
m_net = net;
Packet::EnablePrinting();
m_sendEvent = Simulator::Schedule(Seconds(t), &myCustomApp::Send, this);
m_net->SetReceiveCallback(MakeCallback(&myCustomApp::RxPacket, this));
}
bool
myCustomApp::RxPacket(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender)
{
m_totalRx = m_totalRx + pkt->GetSize();
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << Names::FindName(m_net->GetNode()) << "/" << Names::FindName(m_net) <<" : Pkt rcvd ! " << pkt->ToString());
return true;
}
void
myCustomApp::DoDispose()
{
NS_LOG_FUNCTION(this);
Application::DoDispose();
}
void
myCustomApp::StartApplication()
{
NS_LOG_FUNCTION(this);
}
void
myCustomApp::StopApplication()
{
NS_LOG_FUNCTION(this);
Simulator::Cancel(m_sendEvent);
NS_LOG_INFO("\tNb bytes rcvd :" << GetTotalRx());
}
void
myCustomApp::Send()
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_sendEvent.IsExpired());
uint8_t txBuffer[] = "\"Can you tell me where my country lies?\" \\ said the unifaun to his "
"true love's eyes. \\ \"It lies with me!\" cried the Queen of Maybe \\ - "
"for her merchandise, he traded in his prize.";
size_t txBufferSize = sizeof(txBuffer);
Ptr<Packet> p = Create<Packet>(txBuffer, txBufferSize);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << Names::FindName(m_net->GetNode()) << "/" << Names::FindName(m_net) <<" : Pkt sent ! " << p->ToString());
m_net->Send(p, m_net->GetBroadcast(), 0x800);
m_sendEvent = Simulator::Schedule(Seconds(2), &myCustomApp::Send, this);
}
uint64_t
myCustomApp::GetTotalTx() const
{
return m_totalTx;
}
uint64_t
myCustomApp::GetTotalRx() const
{
return m_totalRx;
}
} // Namespace ns3

View File

@@ -0,0 +1,66 @@
#ifndef MY_CUSTOM_APP_H
#define MY_CUSTOM_APP_H
#include "ns3/application.h"
#include "ns3/event-id.h"
#include "ns3/ptr.h"
#include <ns3/traced-callback.h>
#include "ns3/point-to-point-net-device.h"
namespace ns3
{
class Socket;
class Packet;
class myCustomApp : public Application
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId();
myCustomApp();
~myCustomApp() override;
/**
* \return the total bytes sent by this app
*/
uint64_t GetTotalTx() const;
uint64_t GetTotalRx() const;
void Setup(Ptr<PointToPointNetDevice> net, int t);
bool RxPacket(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender);
protected:
void DoDispose() override;
private:
void StartApplication() override;
void StopApplication() override;
/**
* \brief Send a packet
*/
void Send();
uint32_t m_count; //!< Maximum number of packets the application will send
Time m_interval; //!< Packet inter-send time
uint32_t m_size; //!< Size of the sent packet (including the SeqTsHeader)
uint32_t m_sent; //!< Counter for sent packets
uint64_t m_totalRx; //!< Total bytes sent
uint64_t m_totalTx; //!< Total bytes rcvd
Address m_peerAddress; //!< Remote peer address
uint16_t m_peerPort; //!< Remote peer port
EventId m_sendEvent; //!< Event to send the next packet
Ptr<PointToPointNetDevice> m_net;
};
} // namespace ns3
#endif

View File

@@ -0,0 +1,8 @@
#include "traffic-generator.h"
namespace ns3
{
/* ... */
}

View File

@@ -0,0 +1,20 @@
#ifndef TRAFFIC_GENERATOR_H
#define TRAFFIC_GENERATOR_H
// Add a doxygen group for this module.
// If you have more than one file, this should be in only one of them.
/**
* \defgroup traffic-generator Description of the traffic-generator
*/
namespace ns3
{
// Each class should be documented using Doxygen,
// and have an \ingroup traffic-generator directive
/* ... */
}
#endif /* TRAFFIC_GENERATOR_H */

View File

@@ -0,0 +1,19 @@
#! /usr/bin/env python3
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("traffic-generator-ethernet", "True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = []

View File

@@ -0,0 +1,86 @@
// Include a header file from your module to test.
#include "ns3/traffic-generator.h"
// An essential include is test.h
#include "ns3/test.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;
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup traffic-generator-tests Tests for traffic-generator
* \ingroup traffic-generator
* \ingroup tests
*/
// This is an example TestCase.
/**
* \ingroup traffic-generator-tests
* Test case for feature 1
*/
class TrafficGeneratorTestCase1 : public TestCase
{
public:
TrafficGeneratorTestCase1();
virtual ~TrafficGeneratorTestCase1();
private:
void DoRun() override;
};
// Add some help text to this case to describe what it is intended to test
TrafficGeneratorTestCase1::TrafficGeneratorTestCase1()
: TestCase("TrafficGenerator test case (does nothing)")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TrafficGeneratorTestCase1::~TrafficGeneratorTestCase1()
{
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TrafficGeneratorTestCase1::DoRun()
{
// A wide variety of test macros are available in src/core/test.h
NS_TEST_ASSERT_MSG_EQ(true, true, "true doesn't equal true for some reason");
// Use this one for floating point comparisons
NS_TEST_ASSERT_MSG_EQ_TOL(0.01, 0.01, 0.001, "Numbers are not equal within tolerance");
}
// 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 traffic-generator-tests
* TestSuite for module traffic-generator
*/
class TrafficGeneratorTestSuite : public TestSuite
{
public:
TrafficGeneratorTestSuite();
};
TrafficGeneratorTestSuite::TrafficGeneratorTestSuite()
: TestSuite("traffic-generator", UNIT)
{
// TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
AddTestCase(new TrafficGeneratorTestCase1, TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup traffic-generator-tests
* Static variable for test initialization
*/
static TrafficGeneratorTestSuite strafficGeneratorTestSuite;

View File

@@ -0,0 +1,84 @@
check_include_file_cxx(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
add_definitions(-DHAVE_STDINT_H)
endif()
set(examples_as_tests_sources)
if(${ENABLE_EXAMPLES})
set(examples_as_tests_sources
#test/tsn-examples-test-suite.cc
)
endif()
build_lib(
LIBNAME tsn
SOURCE_FILES model/tsn-node.cc
model/tsn-net-device.cc
model/tsn-aggregated-net-device.cc
model/tsn-multidrop-channel.cc
model/transmission-gate.cc
model/tsn-multidrop-net-device.cc
model/tas.cc
model/tsn-transmission-selection-algo.cc
model/cbs.cc
model/clock.cc
model/clock-constant-drift.cc
model/clock-virtual.cc
model/clock-fix-precision.cc
model/gPTP.cc
model/gPTP-header.cc
model/gPTP-packet.cc
model/stream-identity-entry.cc
model/stream-identification-function.cc
model/stream-identification-function-null.cc
model/stream-identification-function-active-dest-mac-vlan.cc
model/frer-sequence-encode-decode-function.cc
model/frer-sequence-generation-function.cc
model/frer-sequence-recovery-function.cc
model/frer-base-recovery-function.cc
model/frer-match-recovery-function.cc
model/frer-vector-recovery-function.cc
model/frer-latent-error-detection-function.cc
model/psfp-stream-filter-instance.cc
model/psfp-flow-meter-instance.cc
helper/tsn-helper.cc
HEADER_FILES model/tsn-node.h
model/tsn-net-device.h
model/tsn-aggregated-net-device.h
model/tsn-multidrop-channel.h
model/tsn-multidrop-net-device.h
model/transmission-gate.h
model/tas.h
model/tsn-transmission-selection-algo.h
model/cbs.h
model/clock.h
model/clock-constant-drift.h
model/clock-fix-precision.h
model/clock-virtual.h
model/gPTP.h
model/gPTP-header.h
model/gPTP-packet.h
model/stream-identity-entry.h
model/stream-identification-function.h
model/stream-identification-function-null.h
model/stream-identification-function-active-dest-mac-vlan.h
model/frer-sequence-encode-decode-function.h
model/frer-sequence-generation-function.h
model/frer-sequence-recovery-function.h
model/frer-base-recovery-function.h
model/frer-match-recovery-function.h
model/frer-vector-recovery-function.h
model/frer-latent-error-detection-function.h
model/psfp-stream-filter-instance.h
model/psfp-flow-meter-instance.h
helper/tsn-helper.h
LIBRARIES_TO_LINK ${libcore}
${libethernet}
TEST_SOURCES test/tsn-test-suite.cc
test/cbs-test-suite.cc
test/tas-test-suite.cc
test/frer-test-suite.cc
test/psfp-test-suite.cc
test/tsn-multidrop-test-suite.cc
${examples_as_tests_sources}
)

View File

@@ -0,0 +1,36 @@
set(base_examples
tsn-point2point
tsn-point2point-withCBS
tsn-point2point-withTAS
tsn-point2point-withTAS-CBS
tsn-point2point-withTAS-gPTP
tsn-point2point-withTAS-GuardBand
tsn-point2point-withPSFP-MaxSDUSizeFilter
tsn-point2point-withPSFP-FlowMeter
tsn-switched-withFRER
tsn-switched-withFRER-recoveryAlgo
tsn-switched-withFRER-activeSid
tsn-point2point-withGPTP
tsn-point2point-withGPTP-Multidomain
tsn-point2point-withGPTP-fixPrecisionClock
tsn-switched-withGPTP
tsn-multidrop
tsn-multidrop-withCBS
tsn-multidrop-withTAS
tsn-multidrop-withTAS-CBS
tsn-switched-multidrop
)
foreach(
example
${base_examples}
)
build_lib_example(
NAME ${example}
SOURCE_FILES ${example}.cc
LIBRARIES_TO_LINK ${libtsn}
${libcore}
${libnetwork}
${libtraffic-generator}
${libethernet}
)
endforeach()

View File

@@ -0,0 +1,191 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/cbs.h"
/**
* \file
*
* Example of the use of tsn-net-device.cc tsn-multidrop-channel.cc on a network
* composed of four end-stations connected by a 10Base-T1S with a CBS on the
* highest priority FIFO of ES1
* ES1 ====== ES2
* ||
* ||== ES3
* ||
* ||== ES4
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log pkt fifo entry time
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(VID:" << ethHeader.GetVid() << ") entering the FIFO on the producer");
}
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetDevice", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropChannel", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES4", n3);
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
// net0->SetAttribute("PLCAMaxBurstCount", UintegerValue(1));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(net3);
Names::Add("ES4#01", net3);
//Create a 10Base-T1S Channel and attach it two the netDevices
Ptr<TsnMultidropChannel> channel = CreateObject<TsnMultidropChannel>();
net0->Attach(channel);
net1->Attach(channel);
net2->Attach(channel);
net3->Attach(channel);
//Allocate a Mac address and create a FIFO (for the output port)
//for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("8Mb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("10Mb/s")));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
net1->SetAddress(Mac48Address::Allocate());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(2));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("PCP", UintegerValue(0));
app1->SetAttribute("VlanID", UintegerValue(2));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
//Callback to log pkt fifo entry time
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
//Callback to display the packet latency
net0->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
//Execute the simulation
Simulator::Stop(MilliSeconds(5));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,292 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/cbs.h"
/**
* \file
*
* Example of the use of tsn-net-device.cc tsn-multidrop-channel.cc on a network
* composed of four end-stations connected by a 10Base-T1S with TAS and CBS on
* ES1 port
*
* ES1 ====== ES2
* ||
* ||== ES3
* ||
* ||== ES4
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log pkt fifo entry time
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(VID:" << ethHeader.GetVid() << ") entering the FIFO on the producer");
}
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
//A callback to log the PLCA state
static void
PLCAStateCallback(std::string context, TsnMultidropNetDevice::PLCAState state)
{
std::string stateStr = "";
if ( state == TsnMultidropNetDevice::PLCAState::DISABLE)
{
stateStr = "DISABLE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECOVER)
{
stateStr = "RECOVER";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RESYNC)
{
stateStr = "RESYNC";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SEND_BEACON)
{
stateStr = "SEND_BEACON";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SYNCING)
{
stateStr = "SYNCING";
}
else if ( state == TsnMultidropNetDevice::PLCAState::WAIT_TO)
{
stateStr = "WAIT_TO";
}
else if ( state == TsnMultidropNetDevice::PLCAState::COMMIT)
{
stateStr = "COMMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::TRANSMIT)
{
stateStr = "TRANSMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::BURST)
{
stateStr = "BURST";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECEIVE)
{
stateStr = "RECEIVE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::YIELD)
{
stateStr = "YIELD";
}
else if ( state == TsnMultidropNetDevice::PLCAState::ABORT)
{
stateStr = "ABORT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::NEXT_TX_OPPORTUNITY)
{
stateStr = "NEXT_TX_OPPORTUNITY";
}
else if ( state == TsnMultidropNetDevice::PLCAState::EARLY_RECEIVE)
{
stateStr = "EARLY_RECEIVE";
}
else
{
stateStr = "UNKNOWN_STATE";
}
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : PLCAState => " << stateStr);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetDevice", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropChannel", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES4", n3);
//Add perfect clock on each node
n0->AddClock(CreateObject<Clock>());
n1->AddClock(CreateObject<Clock>());
n2->AddClock(CreateObject<Clock>());
n3->AddClock(CreateObject<Clock>());
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
// net0->SetAttribute("PLCAMaxBurstCount", UintegerValue(1));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(net3);
Names::Add("ES4#01", net3);
//Create a 10Base-T1S Channel and attach it two the netDevices
Ptr<TsnMultidropChannel> channel = CreateObject<TsnMultidropChannel>();
net0->Attach(channel);
net1->Attach(channel);
net2->Attach(channel);
net3->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("1300Kb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("10Mb/s")));
cbs->SetAttribute("MultidropMode", BooleanValue(true));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add two GCL entry on net0 and start TAS
net0->AddGclEntry(Time(MilliSeconds(10)), 2);
net0->AddGclEntry(Time(MilliSeconds(4)), 1);
net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(Tas::MTU));
net0->GetTas()->SetAttribute("MultidropMode", BooleanValue(true));
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(2));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
//Callback to log pkt fifo entry time
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
//Callback to display the packet latency
net0->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
// net0->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net0)));
// net1->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net1)));
// net2->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net2)));
// net3->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net3)));
//Execute the simulation
Simulator::Stop(MilliSeconds(20));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,274 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-net-device.cc tsn-multidrop-channel.cc on a network
* composed of four end-stations connected by a 10Base-T1S with TAS on ES1 port
* ES1 ====== ES2
* ||
* ||== ES3
* ||
* ||== ES4
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log pkt fifo entry time
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(VID:" << ethHeader.GetVid() << ") entering the FIFO on the producer");
}
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
//A callback to log the PLCA state
static void
PLCAStateCallback(std::string context, TsnMultidropNetDevice::PLCAState state)
{
std::string stateStr = "";
if ( state == TsnMultidropNetDevice::PLCAState::DISABLE)
{
stateStr = "DISABLE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECOVER)
{
stateStr = "RECOVER";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RESYNC)
{
stateStr = "RESYNC";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SEND_BEACON)
{
stateStr = "SEND_BEACON";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SYNCING)
{
stateStr = "SYNCING";
}
else if ( state == TsnMultidropNetDevice::PLCAState::WAIT_TO)
{
stateStr = "WAIT_TO";
}
else if ( state == TsnMultidropNetDevice::PLCAState::COMMIT)
{
stateStr = "COMMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::TRANSMIT)
{
stateStr = "TRANSMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::BURST)
{
stateStr = "BURST";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECEIVE)
{
stateStr = "RECEIVE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::YIELD)
{
stateStr = "YIELD";
}
else if ( state == TsnMultidropNetDevice::PLCAState::ABORT)
{
stateStr = "ABORT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::NEXT_TX_OPPORTUNITY)
{
stateStr = "NEXT_TX_OPPORTUNITY";
}
else if ( state == TsnMultidropNetDevice::PLCAState::EARLY_RECEIVE)
{
stateStr = "EARLY_RECEIVE";
}
else
{
stateStr = "UNKNOWN_STATE";
}
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : PLCAState => " << stateStr);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetDevice", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropChannel", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES4", n3);
//Add perfect clock on each node
n0->AddClock(CreateObject<Clock>());
n1->AddClock(CreateObject<Clock>());
n2->AddClock(CreateObject<Clock>());
n3->AddClock(CreateObject<Clock>());
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
// net0->SetAttribute("PLCAMaxBurstCount", UintegerValue(1));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("DataRate", DataRateValue(DataRate("10Mb/s")));
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(net3);
Names::Add("ES4#01", net3);
//Create a 10Base-T1S Channel and attach it two the netDevices
Ptr<TsnMultidropChannel> channel = CreateObject<TsnMultidropChannel>();
net0->Attach(channel);
net1->Attach(channel);
net2->Attach(channel);
net3->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
net3->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add two GCL entry on net0 and start TAS
net0->AddGclEntry(Time(MilliSeconds(2)), 2);
net0->AddGclEntry(Time(MilliSeconds(4)), 1);
net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(Tas::MTU));
net0->GetTas()->SetAttribute("MultidropMode", BooleanValue(true));
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(1));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(2));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
//Callback to log pkt fifo entry time
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
//Callback to display the packet latency
net0->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
// net0->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net0)));
// net1->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net1)));
// net2->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net2)));
// net3->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net3)));
//Execute the simulation
Simulator::Stop(MilliSeconds(8));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,254 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-net-device.cc tsn-multidrop-channel.cc on a network
* composed of four end-stations connected by a 10Base-T1S
* ES1 ====== ES2
* ||
* ||== ES3
* ||
* ||== ES4
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log pkt fifo entry time
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(VID:" << ethHeader.GetVid() << ") entering the FIFO on the producer");
}
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
//A callback to log the PLCA state
static void
PLCAStateCallback(std::string context, TsnMultidropNetDevice::PLCAState state)
{
std::string stateStr = "";
if ( state == TsnMultidropNetDevice::PLCAState::DISABLE)
{
stateStr = "DISABLE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECOVER)
{
stateStr = "RECOVER";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RESYNC)
{
stateStr = "RESYNC";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SEND_BEACON)
{
stateStr = "SEND_BEACON";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SYNCING)
{
stateStr = "SYNCING";
}
else if ( state == TsnMultidropNetDevice::PLCAState::WAIT_TO)
{
stateStr = "WAIT_TO";
}
else if ( state == TsnMultidropNetDevice::PLCAState::COMMIT)
{
stateStr = "COMMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::TRANSMIT)
{
stateStr = "TRANSMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::BURST)
{
stateStr = "BURST";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECEIVE)
{
stateStr = "RECEIVE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::YIELD)
{
stateStr = "YIELD";
}
else if ( state == TsnMultidropNetDevice::PLCAState::ABORT)
{
stateStr = "ABORT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::NEXT_TX_OPPORTUNITY)
{
stateStr = "NEXT_TX_OPPORTUNITY";
}
else if ( state == TsnMultidropNetDevice::PLCAState::EARLY_RECEIVE)
{
stateStr = "EARLY_RECEIVE";
}
else
{
stateStr = "UNKNOWN_STATE";
}
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : PLCAState => " << stateStr);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetDevice", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropChannel", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES4", n3);
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
// net0->SetAttribute("PLCAMaxBurstCount", UintegerValue(1));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(net3);
Names::Add("ES4#01", net3);
//Create a 10Base-T1S Channel and attach it two the netDevices
Ptr<TsnMultidropChannel> channel = CreateObject<TsnMultidropChannel>();
net0->Attach(channel);
net1->Attach(channel);
net2->Attach(channel);
net3->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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetAddress(Mac48Address::Allocate());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
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));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net1);
app1->SetAttribute("BurstSize", UintegerValue(2));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
n1->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
//Callback to log pkt fifo entry time
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
//Callback to display the packet latency
net0->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
net0->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net0)));
net1->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net1)));
net2->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net2)));
net3->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net3)));
//Execute the simulation
Simulator::Stop(MilliSeconds(5));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,119 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-net-device.h"
#include "ns3/cbs.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link with CBS on ES1 port
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log the credit value
static void
CbsCallback(std::string context, double credit)
{
Time t = Simulator::Now();
NS_LOG_INFO("FIFO:" << context << " : At "<< t.GetNanoSeconds() <<" credit=" << credit);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add a CBS shaper and a FIFO on net0 only fifo.
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
Names::Add(Names::FindName(net0) + "-" + "0", cbs); //NetDeviceName-FifoNumber
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("5Mb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
//Create and add a FIFO on net1
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(5));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(4));
//Callback to display the packet transmitted and received log
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the credit update log
cbs->TraceConnectWithoutContext("Credit", MakeBoundCallback(&CbsCallback, Names::FindName(cbs)));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,127 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/clock.h"
#include "ns3/clock-constant-drift.h"
#include "ns3/gPTP.h"
/**
* \file
*
* Example of the use of gPTP with two domain on a network composed of two
* end-stations connected by a 1Gb/s full duplex link.
* ES1 is the GPTP Grandmaster for the two domains.
* Is this example, all the clock on the same node are slaved on the main clock
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
static void
PdelayCallback(std::string context, Ptr<TsnNetDevice> net, double pdelay)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << "/" << Names::FindName(net) << " computed pdelay = " << pdelay);
}
static void
ClockAfterCorrectionCallback(std::string context, Time clockValue)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add clock to TsnNode
Ptr<Clock> c0 = CreateObject<Clock>(); //Perfect clock
n0->SetMainClock(c0);
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
c1->SetAttribute("InitialOffset", TimeValue(Seconds(1)));
c1->SetAttribute("DriftRate", DoubleValue(10));
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n1->SetMainClock(c1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
channel->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Add two fifo per netdevice.
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add and configure GPTP
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
gPTP0->SetNode(n0);
gPTP0->SetMainClock(c0);
gPTP0->AddDomain(0);
gPTP0->AddDomain(4);
gPTP0->AddPort(net0, GPTP::MASTER, 0);
gPTP0->AddPort(net0, GPTP::MASTER, 4);
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
gPTP0->SetAttribute("Priority", UintegerValue(1));
n0->AddApplication(gPTP0);
gPTP0->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
gPTP1->SetNode(n1);
gPTP1->SetMainClock(c1);
gPTP1->AddDomain(0);
gPTP1->AddDomain(4);
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
gPTP1->AddPort(net1, GPTP::SLAVE, 4);
gPTP1->SetAttribute("Priority", UintegerValue(1));
n1->AddApplication(gPTP1);
gPTP1->SetStartTime(Seconds(0));
//Callback to displa information about GPTP execution
gPTP1->TraceConnectWithoutContext("Pdelay", MakeBoundCallback(&PdelayCallback, Names::FindName(n1)));
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
//Execute the simulation
Simulator::Stop(Seconds(3));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,123 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/clock.h"
#include "ns3/clock-fix-precision.h"
#include "ns3/gPTP.h"
/**
* \file
*
* Example of the use of gPTP on a network composed of two end-stations
* connected by a 1Gb/s full duplex link. ES1 is the GPTP Grandmaster.
* This exemple use fix precision clock (non realistic clock made for
* research purpose)
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
static void
PdelayCallback(std::string context, Ptr<TsnNetDevice> net, double pdelay)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << "/" << Names::FindName(net) << " computed pdelay = " << pdelay);
}
static void
ClockAfterCorrectionCallback(std::string context, Ptr<Clock> gmClock, Time clockValue)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (gmClock->GetLocalTime()-clockValue).GetNanoSeconds() << "ns)");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
// LogComponentEnable("GPTP", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add clock to TsnNode
Ptr<Clock> c0 = CreateObject<Clock>(); //Perfect clock
n0->SetMainClock(c0);
Ptr<FixPrecisionClock> c1 = CreateObject<FixPrecisionClock>();
c1->SetAttribute("Precision", TimeValue(NanoSeconds(100)));
c1->SetRefClock(c0); //Set GM clock as reference
n1->SetMainClock(c1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
channel->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Add two fifo per netdevice.
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add and configure GPTP
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
gPTP0->SetNode(n0);
gPTP0->SetMainClock(c0);
gPTP0->AddDomain(0);
gPTP0->AddPort(net0, GPTP::MASTER, 0);
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
gPTP0->SetAttribute("Priority", UintegerValue(1));
n0->AddApplication(gPTP0);
gPTP0->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
gPTP1->SetNode(n1);
gPTP1->SetMainClock(c1);
gPTP1->AddDomain(0, c1);
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
gPTP1->SetAttribute("Priority", UintegerValue(1));
n1->AddApplication(gPTP1);
gPTP1->SetStartTime(Seconds(0));
//Callback to displa information about GPTP execution
gPTP1->TraceConnectWithoutContext("Pdelay", MakeBoundCallback(&PdelayCallback, Names::FindName(n1)));
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1), c0));
//Execute the simulation
Simulator::Stop(Seconds(5));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,121 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/clock.h"
#include "ns3/clock-constant-drift.h"
#include "ns3/gPTP.h"
/**
* \file
*
* Example of the use of gPTP on a network composed of two end-stations
* connected by a 1Gb/s full duplex link. ES1 is the GPTP Grandmaster.
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
static void
PdelayCallback(std::string context, Ptr<TsnNetDevice> net, double pdelay)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << "/" << Names::FindName(net) << " computed pdelay = " << pdelay);
}
static void
ClockAfterCorrectionCallback(std::string context, Time clockValue)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add clock to TsnNode
Ptr<Clock> c0 = CreateObject<Clock>(); //Perfect clock
n0->SetMainClock(c0);
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
c1->SetAttribute("InitialOffset", TimeValue(Seconds(1)));
c1->SetAttribute("DriftRate", DoubleValue(10));
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n1->SetMainClock(c1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
channel->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Add two fifo per netdevice.
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add and configure GPTP
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
gPTP0->SetNode(n0);
gPTP0->SetMainClock(c0);
gPTP0->AddDomain(0);
gPTP0->AddPort(net0, GPTP::MASTER, 0);
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
gPTP0->SetAttribute("Priority", UintegerValue(1));
n0->AddApplication(gPTP0);
gPTP0->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
gPTP1->SetNode(n1);
gPTP1->SetMainClock(c1);
gPTP1->AddDomain(0);
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
gPTP1->SetAttribute("Priority", UintegerValue(1));
n1->AddApplication(gPTP1);
gPTP1->SetStartTime(Seconds(0));
//Callback to displa information about GPTP execution
gPTP1->TraceConnectWithoutContext("Pdelay", MakeBoundCallback(&PdelayCallback, Names::FindName(n1)));
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1)));
//Execute the simulation
Simulator::Stop(Seconds(3));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,142 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/stream-identification-function-null.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt transmission
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " entered the transmission FIFO !");
}
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//Callbacks to log pkt drop
static void
MaxSDUSizeFilterDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : Packet #"<< p->GetUid() <<" was dropped by MaxSDUSizeFilter");
}
static void
REDFrameDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : Packet #"<< p->GetUid() <<" was dropped by Flow Meter");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
LogComponentEnable("StreamFilterInstance", LOG_LEVEL_INFO);
LogComponentEnable("FlowMeterInstance", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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>>());
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
uint16_t StreamHandle = 10;
sif0->SetAttribute("VlanID", UintegerValue(100));
sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n1->AddStreamIdentificationFunction(StreamHandle, sif0, {net1}, {}, {}, {});
//PSFP configuration
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard (like in PSFP MIB)
sfi0->SetAttribute("MaxSDUSize", UintegerValue(0));
n1->AddStreamFilter(sfi0);
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
fm0->SetAttribute("CIR", DataRateValue(DataRate("40Mb/s")));
fm0->SetAttribute("CBS", UintegerValue(1000));
fm0->SetAttribute("DropOnYellow", BooleanValue(true));
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(false));
uint16_t fmid = n1->AddFlowMeter(fm0);
sfi0->AddFlowMeterInstanceId(fmid);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(4));
app0->SetAttribute("PayloadSize", UintegerValue(500));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("PCP", UintegerValue(0));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(5));
app0->SetStopTime(Seconds(12));
//Callback to display the packet log
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
net1->TraceConnectWithoutContext("MaxSDUSizeFilterDrop", MakeBoundCallback(&MaxSDUSizeFilterDrop, Names::FindName(net1)));
net1->TraceConnectWithoutContext("REDFrameDrop", MakeBoundCallback(&REDFrameDrop, Names::FindName(net1)));
//Execute the simulation
Simulator::Stop(Seconds(15));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,140 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/stream-identification-function-null.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt transmission
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " entered the transmission FIFO !");
}
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//Callbacks to log pkt drop
static void
MaxSDUSizeFilterDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : Packet #"<< p->GetUid() <<" was dropped by MaxSDUSizeFilter ");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
LogComponentEnable("StreamFilterInstance", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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>>());
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
uint16_t StreamHandle = 10;
sif0->SetAttribute("VlanID", UintegerValue(100));
sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n1->AddStreamIdentificationFunction(StreamHandle, sif0, {net1}, {}, {}, {});
//PSFP configuration
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
sfi0->SetAttribute("StreamHandle", IntegerValue(StreamHandle));
sfi0->SetAttribute("Priority", IntegerValue(-1)); //-1 = wildcard (like in PSFP MIB)
sfi0->SetAttribute("MaxSDUSize", UintegerValue(522));
sfi0->SetAttribute("StreamBlockedDueToOversizeFrameEnable", BooleanValue(true));
n1->AddStreamFilter(sfi0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(500));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("PCP", UintegerValue(0));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(501));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("PCP", UintegerValue(0));
app1->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(4));
app1->SetStopTime(Seconds(5));
//Callback to display the packet log
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
net1->TraceConnectWithoutContext("MaxSDUSizeFilterDrop", MakeBoundCallback(&MaxSDUSizeFilterDrop, Names::FindName(net1)));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,168 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include <bitset>
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/cbs.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link with TAS with CBS on ES1 port
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log the gate update
static void
TasCallback(std::string context, uint8_t states)
{
Time t = Simulator::Now();
std::string str = "";
std::string binary = std::bitset<8>(states).to_string();
std::reverse(binary.begin(), binary.end());
for (int i = 0; i < (int)binary.length(); i++) {
str += " Fifo " + std::to_string(i);
std::string c{binary[i]};
int state = std::stoi(c);
if(state)
{
str += " Open ;";
}
else
{
str += " Close;";
}
}
NS_LOG_INFO(context << " : At "<< t.GetNanoSeconds() <<" Tas gate states update :" << str);
}
//A callback to log the credit value
static void
CbsCallback(std::string context, double credit)
{
Time t = Simulator::Now();
NS_LOG_INFO("FIFO:" << context << " : At "<< t.GetNanoSeconds() <<" credit=" << credit);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("Tas", LOG_LEVEL_INFO);
LogComponentEnable("Clock", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Add perfect clock on each node
n0->AddClock(CreateObject<Clock>());
n1->AddClock(CreateObject<Clock>());
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add the FIFO 0 on each net device with a cbs on net0's FIFO
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
Names::Add(Names::FindName(net0) + "-" + "0", cbs); //NetDeviceName-FifoNumber
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("2Mb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("100Mb/s")));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Create and add the last seven FIFO on each net device
for (int i=0; i<7; i++)
{
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Callback to display the gate update log (must be before StartTas() to display the initial gate states)
net0->GetTas()->TraceConnectWithoutContext("GatesUpdate", MakeBoundCallback(&TasCallback, Names::FindName(net0)));
//Add two GCL entry on net0 and start TAS
net0->AddGclEntry(Time(MilliSeconds(3)), 1);
net0->AddGclEntry(Time(MilliSeconds(17)), 0);
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(50)));
app0->SetAttribute("PCP", UintegerValue(0));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
//Callback to display the packet transmitted and received log
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the credit update log
cbs->TraceConnectWithoutContext("Credit", MakeBoundCallback(&CbsCallback, Names::FindName(cbs)));
//Execute the simulation
Simulator::Stop(MilliSeconds(60));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,199 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include <bitset>
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of the use of the different TAS guard band mode on a 100Mb/s
* full duplex link with TAS on ES1 port
* ES1 ====== ES2
*
* This example script can be run using one of this two following commands
* according to the algorithm needed :
* ./ns3 run <path to the script> -- --none
* ./ns3 run <path to the script> -- --mtu
* ./ns3 run <path to the script> -- --pktsize
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !" << p->ToString());
}
//A callback to log the pkt given to the netDevice by the application
static void
PktSentCallback(std::string context, Ptr<const Packet> p, uint16_t vid)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " given to the netDevice !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log the gate update
static void
TasCallback(std::string context, uint8_t states)
{
Time t = Simulator::Now();
std::string str = "";
std::string binary = std::bitset<8>(states).to_string();
std::reverse(binary.begin(), binary.end());
for (int i = 0; i < (int)binary.length(); i++) {
str += " Fifo " + std::to_string(i);
std::string c{binary[i]};
int state = std::stoi(c);
if(state)
{
str += " Open ;";
}
else
{
str += " Close;";
}
}
NS_LOG_INFO(context << " : At "<< t.GetNanoSeconds() <<" Tas gate states update :" << str);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("Tas", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
//Command line interpretation
bool mtu = false;
bool pktsize = false;
bool none = false;
CommandLine cmd(__FILE__);
cmd.AddValue("mtu", "Enable mtu guard band mode", mtu);
cmd.AddValue("pktsize", "Enable pktsize guard band mode", pktsize);
cmd.AddValue("none", "Enable none guard band mode", none);
cmd.Parse(argc, argv);
if (mtu+pktsize+none > 1)
{
NS_LOG_INFO("Only one guard band mode can be selected");
return 0;
}
else if (mtu+pktsize+none == 0)
{
NS_LOG_INFO("A guard band mode must be selected using '--mtu' or '--pktsize' or '--none' ");
return 0;
}
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Add perfect clock on each node
n0->AddClock(CreateObject<Clock>());
n1->AddClock(CreateObject<Clock>());
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Set Tas Guard Band Mode (MTU is the default GuardBandMode)
if(mtu)
{
net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(Tas::MTU));
}
else if(pktsize)
{
net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(Tas::PKTSIZE));
}
else
{
net0->GetTas()->SetAttribute("GuardBandMode", EnumValue(Tas::NONE));
}
//Callback to display the gate update log (must be before StartTas() to display the initial gate states)
net0->GetTas()->TraceConnectWithoutContext("GatesUpdate", MakeBoundCallback(&TasCallback, Names::FindName(net0)));
//Add two GCL entry on net0 and start TAS
net0->AddGclEntry(Time(NanoSeconds(10)), 2);
net0->AddGclEntry(Time(NanoSeconds(4990)), 0);
net0->AddGclEntry(Time(MicroSeconds(95)), 2);
net0->AddGclEntry(Time(MicroSeconds(9900)), 0);
net0->AddGclEntry(Time(MilliSeconds(10)), 2);
net0->AddGclEntry(Time(MilliSeconds(20)), 0);
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(100));
app0->SetAttribute("Period", TimeValue(MilliSeconds(40)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(4));
//Callback to display the packet transmitted and received log
app0->TraceConnectWithoutContext("PktSent", MakeBoundCallback(&PktSentCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Execute the simulation
Simulator::Stop(MilliSeconds(30));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,191 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include <bitset>
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/gPTP.h"
#include "ns3/clock.h"
#include "ns3/clock-constant-drift.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link with TAS on ES1 port. ES2 is GPTP grandmaster and ES1 is in
* slave state.
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log the gate update
static void
TasCallback(std::string context, uint8_t states)
{
Time t = Simulator::Now();
std::string str = "";
std::string binary = std::bitset<8>(states).to_string();
std::reverse(binary.begin(), binary.end());
for (int i = 0; i < (int)binary.length(); i++) {
str += " Fifo " + std::to_string(i);
std::string c{binary[i]};
int state = std::stoi(c);
if(state)
{
str += " Open ;";
}
else
{
str += " Close;";
}
}
NS_LOG_INFO(context << " : At "<< t.GetNanoSeconds() <<" Tas gate states update :" << str);
}
static void
ClockAfterCorrectionCallback(std::string context, Time clockValue)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (Simulator::Now()-clockValue).GetNanoSeconds() << "ns)");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("TsnNode", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("Tas", LOG_LEVEL_INFO);
LogComponentEnable("Clock", LOG_LEVEL_INFO);
// LogComponentEnable("GPTP", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add clock to TsnNode
Ptr<ConstantDriftClock> c0 = CreateObject<ConstantDriftClock>();
c0->SetAttribute("InitialOffset", TimeValue(Seconds(5)));
c0->SetAttribute("DriftRate", DoubleValue(50));
c0->SetAttribute("Granularity", TimeValue(NanoSeconds(1)));
n0->SetMainClock(c0);
Ptr<Clock> c1 = CreateObject<Clock>(); //Perfect clock for the GM
n1->SetMainClock(c1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
channel->SetAttribute("Delay", TimeValue(NanoSeconds(200)));
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add and configure GPTP
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
gPTP0->SetNode(n0);
gPTP0->SetMainClock(c0);
gPTP0->AddDomain(0);
gPTP0->AddPort(net0, GPTP::SLAVE, 0);
gPTP0->SetAttribute("Priority", UintegerValue(0));
n0->AddApplication(gPTP0);
gPTP0->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
gPTP1->SetNode(n1);
gPTP1->SetMainClock(c1);
gPTP1->AddDomain(0);
gPTP1->AddPort(net1, GPTP::MASTER, 0);
gPTP1->SetAttribute("Priority", UintegerValue(0));
n1->AddApplication(gPTP1);
gPTP1->SetStartTime(Seconds(0));
//Add two GCL entry on net0 and start TAS
//(must be done after GPTP declaration due to clock declaration)
net0->AddGclEntry(Time(MilliSeconds(10)), 1);
net0->AddGclEntry(Time(MilliSeconds(10)), 2);
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(15)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(4));
//Callback to display the packet transmitted and received log
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the gate update log (must be before StartTas() to display the initial gate states)
net0->GetTas()->TraceConnectWithoutContext("GatesUpdate", MakeBoundCallback(&TasCallback, Names::FindName(net0)));
//Callback to monitor synchronization
gPTP0->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n0)));
//Execute the simulation
Simulator::Stop(Seconds(1.5));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,148 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include <bitset>
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link with TAS on ES1 port
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " begin transmission !");
}
//A callback to log the gate update
static void
TasCallback(std::string context, uint8_t states)
{
Time t = Simulator::Now();
std::string str = "";
std::string binary = std::bitset<8>(states).to_string();
std::reverse(binary.begin(), binary.end());
for (int i = 0; i < (int)binary.length(); i++) {
str += " Fifo " + std::to_string(i);
std::string c{binary[i]};
int state = std::stoi(c);
if(state)
{
str += " Open ;";
}
else
{
str += " Close;";
}
}
NS_LOG_INFO(context << " : At "<< t.GetNanoSeconds() <<" Tas gate states update :" << str);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("Tas", LOG_LEVEL_INFO);
LogComponentEnable("Clock", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Add perfect clock on each node
n0->AddClock(CreateObject<Clock>());
n1->AddClock(CreateObject<Clock>());
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Callback to display the gate update log (must be before StartTas() to display the initial gate states)
net0->GetTas()->TraceConnectWithoutContext("GatesUpdate", MakeBoundCallback(&TasCallback, Names::FindName(net0)));
//Add two GCL entry on net0 and start TAS
net0->AddGclEntry(Time(MilliSeconds(10)), 0);
net0->AddGclEntry(Time(MilliSeconds(10)), 2);
net0->StartTas();
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(15)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(4));
//Callback to display the packet transmitted and received log
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Execute the simulation
Simulator::Stop(MilliSeconds(80));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,92 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
/**
* \file
*
* Example of the use of tsn-node.cc tsn-net-device.cc ethernet-channel.cc
* on a network composed of two end-stations connected by a 100Mb/s
* full duplex link
* ES1 ====== ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received !");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("100Mb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
//Create a Ethernet 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(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to display the packet received log
std::string context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, context));
//Execute the simulation
Simulator::Stop(Seconds(10));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,301 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-channel.h"
#include "ns3/switch-net-device.h"
/**
* \file
*
* Example of the use of tsn-net-device.cc tsn-multidrop-channel.cc on a network
* switched network with three end-stations connected by a 10Base-T1S
* ES1 ==== SW ====== ES2
* ||
* ||== ES3
* ||
* ||== ES4
*
* There is two flows on this network :
* - vlanID = 10 from ES1 to ES2
* - vlanID = 20 from ES2 to ES1, ES3, ES4
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log pkt fifo entry time
static void
MacTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << "(VID:" << ethHeader.GetVid() << ") entering the FIFO on the producer");
}
//A callback to log the pkt latency
static void
LatencyCallback(std::string context, Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
Time latency = arrival - tag.GetTimestamp();
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " received from " << ethHeader.GetSrc() << "(VID:" << ethHeader.GetVid() << ") with a latency=" << latency.GetNanoSeconds() <<"ns");
}
//A callback to log the PLCA state
static void
PLCAStateCallback(std::string context, TsnMultidropNetDevice::PLCAState state)
{
std::string stateStr = "";
if ( state == TsnMultidropNetDevice::PLCAState::DISABLE)
{
stateStr = "DISABLE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECOVER)
{
stateStr = "RECOVER";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RESYNC)
{
stateStr = "RESYNC";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SEND_BEACON)
{
stateStr = "SEND_BEACON";
}
else if ( state == TsnMultidropNetDevice::PLCAState::SYNCING)
{
stateStr = "SYNCING";
}
else if ( state == TsnMultidropNetDevice::PLCAState::WAIT_TO)
{
stateStr = "WAIT_TO";
}
else if ( state == TsnMultidropNetDevice::PLCAState::COMMIT)
{
stateStr = "COMMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::TRANSMIT)
{
stateStr = "TRANSMIT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::BURST)
{
stateStr = "BURST";
}
else if ( state == TsnMultidropNetDevice::PLCAState::RECEIVE)
{
stateStr = "RECEIVE";
}
else if ( state == TsnMultidropNetDevice::PLCAState::YIELD)
{
stateStr = "YIELD";
}
else if ( state == TsnMultidropNetDevice::PLCAState::ABORT)
{
stateStr = "ABORT";
}
else if ( state == TsnMultidropNetDevice::PLCAState::NEXT_TX_OPPORTUNITY)
{
stateStr = "NEXT_TX_OPPORTUNITY";
}
else if ( state == TsnMultidropNetDevice::PLCAState::EARLY_RECEIVE)
{
stateStr = "EARLY_RECEIVE";
}
else
{
stateStr = "UNKNOWN_STATE";
}
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : PLCAState => " << stateStr);
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetDevice", LOG_LEVEL_INFO);
// LogComponentEnable("TsnMultidropNetChannel", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("ES4", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("SW", n4);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n4->AddDevice(swnet0);
Names::Add("SW#01", swnet0);
Ptr<TsnMultidropNetDevice> swnet1 = CreateObject<TsnMultidropNetDevice>();
swnet1->SetAttribute("PLCALocalNodeId", UintegerValue(0));
swnet1->SetAttribute("PLCANodeCount", UintegerValue(4));
n4->AddDevice(swnet1);
Names::Add("SW#02", swnet1);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(net3);
Names::Add("ES4#01", net3);
//Create a full-duplex channel
Ptr<EthernetChannel> channel0 = CreateObject<EthernetChannel>();
net0->Attach(channel0);
swnet0->Attach(channel0);
//Create a 10Base-T1S Channel and attach it two the netDevices
Ptr<TsnMultidropChannel> channel1 = CreateObject<TsnMultidropChannel>();
swnet1->Attach(channel1);
net1->Attach(channel1);
net2->Attach(channel1);
net3->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)));
n4->AddDevice(sw);
sw->AddSwitchPort(swnet0);
sw->AddSwitchPort(swnet1);
//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>>());
net2->SetAddress(Mac48Address::Allocate());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetAddress(Mac48Address::Allocate());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetAddress(Mac48Address::Allocate());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet0->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetAddress(Mac48Address::Allocate());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
swnet1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add forwarding table
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net2->GetAddress()), 10, {swnet1});
sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 20, {swnet0});
//Application description
//From switched network to 10Base-T1S
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net2->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(2));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(10));
app0->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//From 10Base-T1S to switched network
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net1);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(100));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(20));
app1->SetAttribute("PCP", UintegerValue(1));
n1->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
//Callback to log pkt fifo entry time
std::string context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("MacTx", MakeBoundCallback(&MacTxCallback, context));
//Callback to display the packet latency
context = Names::FindName(n0) + ":" + Names::FindName(net0);
net0->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n1) + ":" + Names::FindName(net1);
net1->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n2) + ":" + Names::FindName(net2);
net2->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
context = Names::FindName(n3) + ":" + Names::FindName(net3);
net3->TraceConnectWithoutContext("Latency", MakeBoundCallback(&LatencyCallback, context));
//Callback to display PLCA state evolution
swnet1->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(swnet1)));
// net1->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net1)));
// net2->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net2)));
// net3->TraceConnectWithoutContext("PLCAState", MakeBoundCallback(&PLCAStateCallback, Names::FindName(net3)));
//Execute the simulation
Simulator::Stop(MilliSeconds(3));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,358 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/trace-helper.h"
#include <fstream>
#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/stream-identification-function-active-dest-mac-vlan.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"
/**
* \file
*
* Example with 2ES connected 4SW in a 1Gb/s full duplex link to demonstrate
* FRER usage. To be more specific this example use the SW1 to do the replication
* using mulitcast and SW4 to do the elimination. The stream identification is
* done by the active mac dest and vlan 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 and 101 flow use FRER mechanisms.
* To be more specific ESsource send a VLAN 100 packet that is replicated toward
* SW 2 with VLAN 100 and toward SW 3 with VLAN 101. A non replicated packet
* using VLAN 102 cross the network.
*
* / ==== SW2 ==== \
* ESsource ==== SW1 SW4 ==== ESdest
* \ ==== SW3 ==== /
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("ActiveDestMacVlanStreamIdentificationFunction", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create six nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("SW3", n4);
Ptr<TsnNode> n5 = CreateObject<TsnNode>();
Names::Add("SW4", n5);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Names::Add("ESsource#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ESdest#01", net1);
Ptr<TsnNetDevice> net2_1 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_1);
Ptr<TsnNetDevice> net2_2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_2);
Ptr<TsnNetDevice> net2_3 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_3);
Ptr<TsnNetDevice> net3_1 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_1);
Ptr<TsnNetDevice> net3_2 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_2);
Ptr<TsnNetDevice> net4_1 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_1);
Ptr<TsnNetDevice> net4_2 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_2);
Ptr<TsnNetDevice> net5_1 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_1);
Ptr<TsnNetDevice> net5_2 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_2);
Ptr<TsnNetDevice> net5_3 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_3);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net0->Attach(l0);
net2_1->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_2->Attach(l1);
net3_1->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_3->Attach(l2);
net4_1->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3_2->Attach(l3);
net5_1->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
l4->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4_2->Attach(l4);
net5_2->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
l5->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net5_3->Attach(l5);
net1->Attach(l5);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
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<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(net3_1);
sw2->AddSwitchPort(net3_2);
Ptr<SwitchNetDevice> sw3 = CreateObject<SwitchNetDevice>();
sw3->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw3->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n4->AddDevice(sw3);
sw3->AddSwitchPort(net4_1);
sw3->AddSwitchPort(net4_2);
Ptr<SwitchNetDevice> sw4 = CreateObject<SwitchNetDevice>();
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<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//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"), 101, {net4_2});
sw4->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {net5_3});
sw4->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {net5_3});
sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 102, {net2_2});
sw2->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 102, {net3_2});
sw4->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 102, {net5_3});
//Stream Indentification + FRER
//First switch
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({StreamHandle});
n2->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqEnc0->SetAttribute("Active", BooleanValue(true));
seqEnc0->SetStreamHandle({StreamHandle});
seqEnc0->SetPort(net2_1);
n2->AddSequenceEncodeDecodeFunction(seqEnc0);
//Active Stream identification to change the vlan on net2_3
Ptr<ActiveDestMacVlanStreamIdentificationFunction> sif1 = CreateObject<ActiveDestMacVlanStreamIdentificationFunction>();
sif1->SetAttribute("VlanID", UintegerValue(100));
sif1->SetAttribute("UpdateVlanID", UintegerValue(101));
sif1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
sif1->SetAttribute("UpdateAddress", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
sif1->SetAttribute("UpdatePCP", UintegerValue(1));
n2->AddStreamIdentificationFunction(StreamHandle, sif1, {}, {}, {}, {net2_3});
//Last switch
//Stream identification
Ptr<NullStreamIdentificationFunction> sif2 = CreateObject<NullStreamIdentificationFunction>();
StreamHandle = 1;
sif2->SetAttribute("VlanID", UintegerValue(100));
sif2->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n5->AddStreamIdentificationFunction(StreamHandle, sif2, {}, {}, {net5_3}, {});
Ptr<NullStreamIdentificationFunction> sif3 = CreateObject<NullStreamIdentificationFunction>();
sif3->SetAttribute("VlanID", UintegerValue(101));
sif3->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n5->AddStreamIdentificationFunction(StreamHandle, sif3, {}, {}, {net5_3}, {});
//Sequence Decode
Ptr<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
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<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
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<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Active stream identification to change back vlanId (optional)
Ptr<ActiveDestMacVlanStreamIdentificationFunction> sif4 = CreateObject<ActiveDestMacVlanStreamIdentificationFunction>();
sif4->SetAttribute("VlanID", UintegerValue(101));
sif4->SetAttribute("UpdateVlanID", UintegerValue(100));
sif4->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
sif4->SetAttribute("UpdateAddress", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
sif4->SetAttribute("UpdatePCP", UintegerValue(1));
n5->AddStreamIdentificationFunction(StreamHandle, sif4, {}, {}, {}, {net5_3});
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(10)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(MilliSeconds(20)));
app1->SetAttribute("PCP", UintegerValue(0));
app1->SetAttribute("VlanID", UintegerValue(102));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(MilliSeconds(41));
//Callback to display the packet transmitted and received log
//Packet::EnablePrinting();
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the packet elimination on last output port
net5_3->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(net5_3)));
//Enable pcap generation
PcapHelper pcapHelper;
std::string pcapFilename;
std::string prefix = "Active-stream-identification";
Ptr<PcapFileWrapper> file;
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net0);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net0, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net2_2);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net2_2, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net2_3);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net2_3, "Sniffer", file);
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net1);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net1, "Sniffer", file);
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,362 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#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"
#include "ns3/frer-vector-recovery-function.h"
/**
* \file
*
* Example with 3ES connected 4SW in a 1Gb/s full duplex link to demonstrate
* the difference between two FRER sub-mechanisms : the match recovery algorithm
* and the vector recovery algorithm. To be more specific this example use the
* SW1 to do the replication using mulitcast and SW4 to do the elimination for
* the flow with VlanId = 100. 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. The Vlan 100 flow go from
* ESsource to ESdest and Vlan 101 flow go from ESPerturbation1 to ESdest.
*
* ESPerturbation1
* ||
* / ==== SW2 ==== \
* ESsource ==== SW1 SW4 ==== ESdest
* \ ==== SW3 ==== /
*
* When using match recovery algorithm, we can note that some packet are received
* multiple times (pkt #2, #3, #4, #5, ..., #9) because Vlan 100 flow is bursty
* and disrupt by the Vlan 101 flow. This behavior is not observed with vector
* recovery algorithm because it store multiple FRER sequence number to do
* the elimination.
*
*
* This example script can be run using one of this two following commands
* according to the algorithm needed :
* ./ns3 run <path to the script> -- --match
* ./ns3 run <path to the script> -- --vector
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
LogComponentEnable("VectorRecoveryFunction", LOG_LEVEL_INFO);
LogComponentEnable("MatchRecoveryFunction", LOG_LEVEL_INFO);
//Command line interpretation
bool enableMatch = false;
bool enableVector = false;
CommandLine cmd(__FILE__);
cmd.AddValue("match", "Enable match recovery algorithm", enableMatch);
cmd.AddValue("vector", "Enable vector recovery algorithm", enableVector);
cmd.Parse(argc, argv);
if (enableMatch && enableVector)
{
NS_LOG_INFO("Only one recovery algorithm can be selected");
return 0;
}
else if (!enableMatch && !enableVector)
{
NS_LOG_INFO("A recovery algorithm must be selected using '--match' or '--vector' ");
return 0;
}
//Create seven nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("SW3", n4);
Ptr<TsnNode> n5 = CreateObject<TsnNode>();
Names::Add("SW4", n5);
Ptr<TsnNode> n6 = CreateObject<TsnNode>();
Names::Add("ESPerturbation1", n6);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Names::Add("ESsource#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ESdest#01", net1);
Ptr<TsnNetDevice> net6 = CreateObject<TsnNetDevice>();
n6->AddDevice(net6);
Names::Add("ESPerturbation1#01", net6);
Ptr<TsnNetDevice> net2_1 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_1);
Ptr<TsnNetDevice> net2_2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_2);
Ptr<TsnNetDevice> net2_3 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_3);
Ptr<TsnNetDevice> net3_1 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_1);
Ptr<TsnNetDevice> net3_2 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_2);
Ptr<TsnNetDevice> net3_3 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_3);
Ptr<TsnNetDevice> net4_1 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_1);
Ptr<TsnNetDevice> net4_2 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_2);
Ptr<TsnNetDevice> net5_1 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_1);
Ptr<TsnNetDevice> net5_2 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_2);
Ptr<TsnNetDevice> net5_3 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_3);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net0->Attach(l0);
net2_1->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_2->Attach(l1);
net3_1->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_3->Attach(l2);
net4_1->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3_2->Attach(l3);
net5_1->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
l4->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4_2->Attach(l4);
net5_2->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
l5->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net5_3->Attach(l5);
net1->Attach(l5);
Ptr<EthernetChannel> l6 = CreateObject<EthernetChannel>();
l6->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3_3->Attach(l6);
net6->Attach(l6);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
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<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(net3_1);
sw2->AddSwitchPort(net3_2);
sw2->AddSwitchPort(net3_3);
Ptr<SwitchNetDevice> sw3 = CreateObject<SwitchNetDevice>();
sw3->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw3->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n4->AddDevice(sw3);
sw3->AddSwitchPort(net4_1);
sw3->AddSwitchPort(net4_2);
Ptr<SwitchNetDevice> sw4 = CreateObject<SwitchNetDevice>();
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<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net6->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//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});
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<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({StreamHandle});
n2->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
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<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
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<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
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<BaseRecoveryFunction> recf0 = nullptr;
if (enableVector){
recf0 = CreateObject<VectorRecoveryFunction>();
recf0->SetAttribute("HistoryLenght", UintegerValue(10));
}
else if(enableMatch)
{
recf0 = CreateObject<MatchRecoveryFunction>();
}
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(10));
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<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net6);
app1->SetAttribute("BurstSize", UintegerValue(3));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(MilliSeconds(20)));
app1->SetAttribute("PCP", UintegerValue(2));
app1->SetAttribute("VlanID", UintegerValue(101));
app1->SetAttribute("Offset", TimeValue(MicroSeconds(40)));
n6->AddApplication(app1);
app1->SetStartTime(MicroSeconds(0));
app1->SetStopTime(MilliSeconds(20));
//Callback to display the packet transmitted and received log
//Packet::EnablePrinting();
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net6->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n6) + ":" + Names::FindName(net6)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the packet elimination on last output port
net5_3->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(net5_3)));
//Execute the simulation
Simulator::Stop(MilliSeconds(40));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,320 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/trace-helper.h"
#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"
/**
* \file
*
* Example with 2ES connected 4SW in a 1Gb/s full duplex link to demonstrate
* FRER usage. To be more specific this example 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.
*
* / ==== SW2 ==== \
* ESsource ==== SW1 SW4 ==== ESdest
* \ ==== SW3 ==== /
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("TsnNetDevice", LOG_LEVEL_INFO);
LogComponentEnable("StreamIdEntry", LOG_LEVEL_INFO);
LogComponentEnable("NullStreamIdentificationFunction", LOG_LEVEL_INFO);
LogComponentEnable("SequenceGenerationFunction", LOG_LEVEL_INFO);
LogComponentEnable("SequenceEncodeDecodeFunction", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create six nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
Names::Add("SW3", n4);
Ptr<TsnNode> n5 = CreateObject<TsnNode>();
Names::Add("SW4", n5);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Names::Add("ESsource#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
Names::Add("ESdest#01", net1);
Ptr<TsnNetDevice> net2_1 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_1);
Names::Add("SW1#01", net2_1);
Ptr<TsnNetDevice> net2_2 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_2);
Ptr<TsnNetDevice> net2_3 = CreateObject<TsnNetDevice>();
n2->AddDevice(net2_3);
Ptr<TsnNetDevice> net3_1 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_1);
Ptr<TsnNetDevice> net3_2 = CreateObject<TsnNetDevice>();
n3->AddDevice(net3_2);
Ptr<TsnNetDevice> net4_1 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_1);
Ptr<TsnNetDevice> net4_2 = CreateObject<TsnNetDevice>();
n4->AddDevice(net4_2);
Ptr<TsnNetDevice> net5_1 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_1);
Ptr<TsnNetDevice> net5_2 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_2);
Ptr<TsnNetDevice> net5_3 = CreateObject<TsnNetDevice>();
n5->AddDevice(net5_3);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net0->Attach(l0);
net2_1->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_2->Attach(l1);
net3_1->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net2_3->Attach(l2);
net4_1->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net3_2->Attach(l3);
net5_1->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
l4->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net4_2->Attach(l4);
net5_2->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
l5->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
net5_3->Attach(l5);
net1->Attach(l5);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
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<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(net3_1);
sw2->AddSwitchPort(net3_2);
Ptr<SwitchNetDevice> sw3 = CreateObject<SwitchNetDevice>();
sw3->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw3->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n4->AddDevice(sw3);
sw3->AddSwitchPort(net4_1);
sw3->AddSwitchPort(net4_2);
Ptr<SwitchNetDevice> sw4 = CreateObject<SwitchNetDevice>();
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<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net4_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net5_3->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//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<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({StreamHandle});
n2->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
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<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
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<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
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<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
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<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(10)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
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(41));
//Callback to display the packet transmitted and received log
//Packet::EnablePrinting();
net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(net0)));
net1->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(net1)));
//Callback to display the packet elimination on last output port
net5_3->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(net5_3)));
PcapHelper pcapHelper;
std::string pcapFilename;
std::string prefix = "example";
Ptr<PcapFileWrapper> file;
pcapFilename = pcapHelper.GetFilenameFromDevice(prefix, net2_2);
file = pcapHelper.CreateFile(pcapFilename, std::ios::out, PcapHelper::DLT_EN10MB);
pcapHelper.HookDefaultSink<EthernetNetDevice>(net2_2, "Sniffer", file);
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,285 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-aggregated-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"
/**
* \file
*
* Example with 2ES connected 2SW in a 1Gb/s full duplex link to demonstrate
* FRER usage when replication and elimation are done on the ES. To be more
* specific this example use the ESsource to do the replication using mulitcast
* and ESdest 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.
*
* / ==== SW1 ==== \
* ESsource ESdest
* \ ==== SW2 ==== /
*
* Although mutliple ways of implementing elimination and replication on ES are
* describe in the standard (IEEE802.1CB-2017 - Annex C), here we implement the
* one describe in "IEEE802.1CB-2017 - Annex C - C.1 Example 1: End-to-end FRER"
* . This ways is based on the use of IEEE802.1AX for port aggregation.
*
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
LogComponentEnable("TsnAggregatedNetDevice", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create six nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> n0_net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(n0_net0);
Names::Add("ESsource#01", n0_net0);
Ptr<TsnNetDevice> n0_net1 = CreateObject<TsnNetDevice>();
n0->AddDevice(n0_net1);
Names::Add("ESsource#02", n0_net1);
Ptr<TsnAggregatedNetDevice> n0_netagg = CreateObject<TsnAggregatedNetDevice>();
n0_netagg->AddNetDevice(n0_net0);
n0_netagg->AddNetDevice(n0_net1);
n0->AddDevice(n0_netagg);
Ptr<TsnNetDevice> n1_net0 = CreateObject<TsnNetDevice>();
n1->AddDevice(n1_net0);
Names::Add("ESdest#01", n1_net0);
Ptr<TsnNetDevice> n1_net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(n1_net1);
Names::Add("ESdest#02", n1_net1);
Ptr<TsnAggregatedNetDevice> n1_netagg = CreateObject<TsnAggregatedNetDevice>();
n1_netagg->AddNetDevice(n1_net0);
n1_netagg->AddNetDevice(n1_net1);
n1->AddDevice(n1_netagg);
Ptr<TsnNetDevice> n2_net0 = CreateObject<TsnNetDevice>();
n2->AddDevice(n2_net0);
Ptr<TsnNetDevice> n2_net1 = CreateObject<TsnNetDevice>();
n2->AddDevice(n2_net1);
Ptr<TsnNetDevice> n3_net0 = CreateObject<TsnNetDevice>();
n3->AddDevice(n3_net0);
Ptr<TsnNetDevice> n3_net1 = CreateObject<TsnNetDevice>();
n3->AddDevice(n3_net1);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n0_net0->Attach(l0);
n2_net0->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n2_net1->Attach(l1);
n1_net0->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n0_net1->Attach(l2);
n3_net0->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n3_net1->Attach(l3);
n1_net1->Attach(l3);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
sw1->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw1->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n2->AddDevice(sw1);
sw1->AddSwitchPort(n2_net0);
sw1->AddSwitchPort(n2_net1);
Ptr<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(n3_net0);
sw2->AddSwitchPort(n3_net1);
//Allocate a Mac address
n0_net0->SetAddress(Mac48Address::Allocate());
n0_net1->SetAddress(Mac48Address::Allocate());
n1_net0->SetAddress(Mac48Address::Allocate());
n1_net1->SetAddress(Mac48Address::Allocate());
sw1->SetAddress(Mac48Address::Allocate());
sw2->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
n0_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n0_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n2_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n2_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n3_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n3_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table
sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n2_net1});
sw2->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n3_net1});
sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {n2_net1});
//Stream Indentification + FRER
//On aggregated interface of ESsource
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
uint16_t StreamHandle = 1;
sif0->SetAttribute("VlanID", UintegerValue(100));
sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n0->AddStreamIdentificationFunction(StreamHandle, sif0, {}, {}, {n0_netagg}, {});
//Sequencing : Sequence generation
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(true)); //out-facing
seqf0->SetStreamHandle({StreamHandle});
n0->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc0->SetAttribute("Direction", BooleanValue(true)); //out-facing
seqEnc0->SetAttribute("Active", BooleanValue(true));
seqEnc0->SetStreamHandle({StreamHandle});
seqEnc0->SetPort(n0_netagg);
n0->AddSequenceEncodeDecodeFunction(seqEnc0);
// On aggregated interface of ESdest
//Stream identification
Ptr<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
StreamHandle = 1;
sif1->SetAttribute("VlanID", UintegerValue(100));
sif1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n1->AddStreamIdentificationFunction(StreamHandle, sif1, {n1_netagg}, {}, {}, {});
//Sequence Decode
Ptr<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc1->SetAttribute("Direction", BooleanValue(true)); //out-facing
seqEnc1->SetAttribute("Active", BooleanValue(false));
seqEnc1->SetStreamHandle({StreamHandle});
seqEnc1->SetPort(n1_netagg);
n1->AddSequenceEncodeDecodeFunction(seqEnc1);
//Sequencing : Sequence recovery
Ptr<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
seqfreco0->SetAttribute("Direction", BooleanValue(true)); //out-facing
seqfreco0->SetAttribute("TakeNoSequence", BooleanValue(false));
seqfreco0->SetAttribute("IndividualRecovery", BooleanValue(false));
seqfreco0->SetStreamHandle({StreamHandle});
seqfreco0->SetPorts({n1_netagg});
n1->AddSequenceRecoveryFunction(seqfreco0);
//Sequencing : Sequence recovery : recovery function
Ptr<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(n0_netagg);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(10)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(n0_net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
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(41));
//Callback to display the packet transmitted and received log
Packet::EnablePrinting();
n0_net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(n0_net0)));
n0_net1->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(n0_net1)));
n1_netagg->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(n1_netagg)));
//Callback to display the packet elimination on last output port
n1_netagg->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(n1_netagg)));
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,324 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#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"
/**
* \file
*
* Example with 2ES connected 2SW in a 1Gb/s full duplex link to demonstrate
* FRER usage when replication and elimation are done on the ES. To be more
* specific this example use the ESsource to do the replication using mulitcast
* and ESdest 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.
*
* / ==== SW1 ==== \
* ESsource ESdest
* \ ==== SW2 ==== /
*
* Although mutliple ways of implementing elimination and replication on ES are
* describe in the standard (IEEE802.1CB-2017 - Annex C), here we implement the
* one describe in "IEEE802.1CB-2017 - Annex C - C.6 Example 6: Chained two-port
* end systems". This ways is based on the use of a 3-port switch integrated
* into the ES to interface between the ES and the network as described below
*
* /===
* ES == Integrated SW
* \===
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
//A callback to log the pkt reception
static void
MacRxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") received !");
}
//A callback to log the pkt emission
static void
PhyTxCallback(std::string context, Ptr<const Packet> p)
{
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << context << " : Pkt #" << p->GetUid() << " (VID:" << ethHeader.GetVid() << ") begin transmission !");
}
//A callback to log pkt elimination
static void
FrerDrop(std::string context, Ptr<const Packet> p)
{
NS_LOG_INFO(context << " : An instance of Packet #"<< p->GetUid() <<" was dropped by FRER recovery function");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("EthernetGenerator", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create six nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ESsource", n0);
Ptr<TsnNode> n0_SW = CreateObject<TsnNode>();
Names::Add("ESsource_SW", n0_SW);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ESdest", n1);
Ptr<TsnNode> n1_SW = CreateObject<TsnNode>();
Names::Add("ESdest_SW", n1_SW);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("SW1", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW2", n3);
//Create and add a netDevices to each node
Ptr<TsnNetDevice> n0_net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(n0_net0);
Names::Add("ESsource#01", n0_net0);
Ptr<TsnNetDevice> n0_SW_net0 = CreateObject<TsnNetDevice>();
n0_SW->AddDevice(n0_SW_net0);
Names::Add("ESsource_SW#01", n0_SW_net0);
Ptr<TsnNetDevice> n0_SW_net1 = CreateObject<TsnNetDevice>();
n0_SW->AddDevice(n0_SW_net1);
Names::Add("ESsource_SW#02", n0_SW_net1);
Ptr<TsnNetDevice> n0_SW_net2 = CreateObject<TsnNetDevice>();
n0_SW->AddDevice(n0_SW_net2);
Names::Add("ESsource_SW#03", n0_SW_net2);
Ptr<TsnNetDevice> n1_net0 = CreateObject<TsnNetDevice>();
n1->AddDevice(n1_net0);
Names::Add("ESdest#01", n1_net0);
Ptr<TsnNetDevice> n1_SW_net0 = CreateObject<TsnNetDevice>();
n1_SW->AddDevice(n1_SW_net0);
Names::Add("ESdest_SW#01", n1_SW_net0);
Ptr<TsnNetDevice> n1_SW_net1 = CreateObject<TsnNetDevice>();
n1_SW->AddDevice(n1_SW_net1);
Names::Add("ESdest_SW#02", n1_SW_net1);
Ptr<TsnNetDevice> n1_SW_net2 = CreateObject<TsnNetDevice>();
n1_SW->AddDevice(n1_SW_net2);
Names::Add("ESdest_SW#03", n1_SW_net2);
Ptr<TsnNetDevice> n2_net0 = CreateObject<TsnNetDevice>();
n2->AddDevice(n2_net0);
Ptr<TsnNetDevice> n2_net1 = CreateObject<TsnNetDevice>();
n2->AddDevice(n2_net1);
Ptr<TsnNetDevice> n3_net0 = CreateObject<TsnNetDevice>();
n3->AddDevice(n3_net0);
Ptr<TsnNetDevice> n3_net1 = CreateObject<TsnNetDevice>();
n3->AddDevice(n3_net1);
//Create Ethernet Channels and attach it to the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n0_net0->Attach(l0);
n0_SW_net0->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n0_SW_net1->Attach(l1);
n2_net0->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n0_SW_net2->Attach(l2);
n3_net0->Attach(l2);
Ptr<EthernetChannel> l3 = CreateObject<EthernetChannel>();
l3->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n1_net0->Attach(l3);
n1_SW_net0->Attach(l3);
Ptr<EthernetChannel> l4 = CreateObject<EthernetChannel>();
l4->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n1_SW_net1->Attach(l4);
n2_net1->Attach(l4);
Ptr<EthernetChannel> l5 = CreateObject<EthernetChannel>();
l5->SetAttribute("Delay", TimeValue(MicroSeconds(0)));
n1_SW_net2->Attach(l5);
n3_net1->Attach(l5);
//Create and add switche net devices to the switch nodes
Ptr<SwitchNetDevice> n0_SW_sw = CreateObject<SwitchNetDevice>();
n0_SW_sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(0)));
n0_SW_sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(0)));
n0_SW->AddDevice(n0_SW_sw);
n0_SW_sw->AddSwitchPort(n0_SW_net0);
n0_SW_sw->AddSwitchPort(n0_SW_net1);
n0_SW_sw->AddSwitchPort(n0_SW_net2);
Ptr<SwitchNetDevice> n1_SW_sw = CreateObject<SwitchNetDevice>();
n1_SW_sw->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(0)));
n1_SW_sw->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(0)));
n1_SW->AddDevice(n1_SW_sw);
n1_SW_sw->AddSwitchPort(n1_SW_net0);
n1_SW_sw->AddSwitchPort(n1_SW_net1);
n1_SW_sw->AddSwitchPort(n1_SW_net2);
Ptr<SwitchNetDevice> sw1 = CreateObject<SwitchNetDevice>();
sw1->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw1->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n2->AddDevice(sw1);
sw1->AddSwitchPort(n2_net0);
sw1->AddSwitchPort(n2_net1);
Ptr<SwitchNetDevice> sw2 = CreateObject<SwitchNetDevice>();
sw2->SetAttribute("MinForwardingLatency", TimeValue(MicroSeconds(10)));
sw2->SetAttribute("MaxForwardingLatency", TimeValue(MicroSeconds(10)));
n3->AddDevice(sw2);
sw2->AddSwitchPort(n3_net0);
sw2->AddSwitchPort(n3_net1);
//Allocate a Mac address
n0_net0->SetAddress(Mac48Address::Allocate());
n1_net0->SetAddress(Mac48Address::Allocate());
n0_SW_sw->SetAddress(Mac48Address::Allocate());
n1_SW_sw->SetAddress(Mac48Address::Allocate());
sw1->SetAddress(Mac48Address::Allocate());
sw2->SetAddress(Mac48Address::Allocate());
//Create and add eight FIFO on each net device
for (int i=0; i<8; i++)
{
n0_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n0_SW_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n0_SW_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n0_SW_net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_SW_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_SW_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n1_SW_net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
n2_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n2_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
n3_net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
n3_net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
}
//Add forwarding table
n0_SW_sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n0_SW_net1, n0_SW_net2});
sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n2_net1});
sw2->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n3_net1});
n1_SW_sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 100, {n1_SW_net0});
n0_SW_sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {n0_SW_net1});
sw1->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {n2_net1});
n1_SW_sw->AddForwardingTableEntry(Mac48Address("ff:ff:ff:ff:ff:ff"), 101, {n1_SW_net0});
//Stream Indentification + FRER
//Integrated SW on ESsource
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
uint16_t StreamHandle = 1;
sif0->SetAttribute("VlanID", UintegerValue(100));
sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n0_SW->AddStreamIdentificationFunction(StreamHandle, sif0, {n0_SW_net0}, {}, {}, {});
//Sequencing : Sequence generation
Ptr<SequenceGenerationFunction> seqf0 = CreateObject<SequenceGenerationFunction>();
seqf0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqf0->SetStreamHandle({StreamHandle});
n0_SW->AddSequenceGenerationFunction(seqf0);
//Sequence encode
Ptr<SequenceEncodeDecodeFunction> seqEnc0 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqEnc0->SetAttribute("Active", BooleanValue(true));
seqEnc0->SetStreamHandle({StreamHandle});
seqEnc0->SetPort(n0_SW_net0);
n0_SW->AddSequenceEncodeDecodeFunction(seqEnc0);
//Integrated SW on ESdest
//Stream identification
Ptr<NullStreamIdentificationFunction> sif1 = CreateObject<NullStreamIdentificationFunction>();
StreamHandle = 1;
sif1->SetAttribute("VlanID", UintegerValue(100));
sif1->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n1_SW->AddStreamIdentificationFunction(StreamHandle, sif1, {}, {}, {n1_SW_net0}, {});
//Sequence Decode
Ptr<SequenceEncodeDecodeFunction> seqEnc1 = CreateObject<SequenceEncodeDecodeFunction>();
seqEnc1->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqEnc1->SetAttribute("Active", BooleanValue(false));
seqEnc1->SetStreamHandle({StreamHandle});
seqEnc1->SetPort(n1_SW_net0);
n1_SW->AddSequenceEncodeDecodeFunction(seqEnc1);
//Sequencing : Sequence recovery
Ptr<SequenceRecoveryFunction> seqfreco0 = CreateObject<SequenceRecoveryFunction>();
seqfreco0->SetAttribute("Direction", BooleanValue(false)); //in-facing
seqfreco0->SetAttribute("TakeNoSequence", BooleanValue(false));
seqfreco0->SetAttribute("IndividualRecovery", BooleanValue(false));
seqfreco0->SetStreamHandle({StreamHandle});
seqfreco0->SetPorts({n1_SW_net0});
n1_SW->AddSequenceRecoveryFunction(seqfreco0);
//Sequencing : Sequence recovery : recovery function
Ptr<MatchRecoveryFunction> recf0 = CreateObject<MatchRecoveryFunction>();
seqfreco0->SetRecoveryFunction(recf0);
//Sequencing : Sequence recovery : latent error detection function
Ptr<LatentErrorDetectionFunction> latf0 = CreateObject<LatentErrorDetectionFunction>();
latf0->SetAttribute("LatentErrorPaths", UintegerValue(2));
latf0->SetRecoveryFunction(recf0);
seqfreco0->SetLatentErrorDetectionFunction(latf0);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(n0_net0);
app0->SetAttribute("BurstSize", UintegerValue(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(MilliSeconds(10)));
app0->SetAttribute("PCP", UintegerValue(1));
app0->SetAttribute("VlanID", UintegerValue(100));
n0->AddApplication(app0);
app0->SetStartTime(MilliSeconds(0));
app0->SetStopTime(MilliSeconds(20));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(n0_net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
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(41));
//Callback to display the packet transmitted and received log
//Packet::EnablePrinting();
n0_net0->TraceConnectWithoutContext("PhyTxBegin", MakeBoundCallback(&PhyTxCallback, Names::FindName(n0) + ":" + Names::FindName(n0_net0)));
n1_net0->TraceConnectWithoutContext("MacRx", MakeBoundCallback(&MacRxCallback, Names::FindName(n1) + ":" + Names::FindName(n1_net0)));
//Callback to display the packet elimination on last output port
n1_SW_net0->TraceConnectWithoutContext("FrerDrop", MakeBoundCallback(&FrerDrop, Names::FindName(n1_SW_net0)));
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,209 @@
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/command-line.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/switch-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/clock.h"
#include "ns3/clock-constant-drift.h"
#include "ns3/gPTP.h"
/**
* \file
*
* Example of the use of gPTP on a network composed of three end-stations
* and connected by a 1Gb/s full duplex network through a switch. ES1 is the
* GPTP Grandmaster.
* ES1 === SW1 === ES3
* ||
* ES2
*/
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("Example");
static void
PdelayCallback(std::string context, Ptr<TsnNetDevice> net, double pdelay)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << "/" << Names::FindName(net) << " computed pdelay = " << pdelay);
}
static void
ClockAfterCorrectionCallback(std::string context, Ptr<Clock> gmClock, Time clockValue)
{
NS_LOG_INFO("[GPTP] At " << Simulator::Now() << " on "<< context << " clock value after correction = " << clockValue.GetNanoSeconds() << "ns (error = "<< (gmClock->GetLocalTime()-clockValue).GetNanoSeconds() << "ns)");
}
int
main(int argc, char* argv[])
{
//Enable logging
LogComponentEnable("Example", LOG_LEVEL_INFO);
LogComponentEnable("TsnNode", LOG_LEVEL_INFO);
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
//Create nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Names::Add("ES1", n0);
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Names::Add("ES2", n1);
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Names::Add("ES3", n2);
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Names::Add("SW1", n3);
//Create and add clock to TsnNode
Ptr<ConstantDriftClock> c0 = CreateObject<ConstantDriftClock>();
c0->SetAttribute("InitialOffset", TimeValue(Seconds(112)));
c0->SetAttribute("DriftRate", DoubleValue(-15));
c0->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n0->SetMainClock(c0);
Ptr<ConstantDriftClock> c1 = CreateObject<ConstantDriftClock>();
c1->SetAttribute("InitialOffset", TimeValue(Seconds(3)));
c1->SetAttribute("DriftRate", DoubleValue(10));
c1->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n1->SetMainClock(c1);
Ptr<ConstantDriftClock> c2 = CreateObject<ConstantDriftClock>();
c2->SetAttribute("InitialOffset", TimeValue(Seconds(1.5)));
c2->SetAttribute("DriftRate", DoubleValue(-8));
c2->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n2->SetMainClock(c2);
Ptr<ConstantDriftClock> c3 = CreateObject<ConstantDriftClock>();
c3->SetAttribute("InitialOffset", TimeValue(Seconds(5)));
c3->SetAttribute("DriftRate", DoubleValue(-5));
c3->SetAttribute("Granularity", TimeValue(NanoSeconds(10)));
n3->SetMainClock(c3);
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n0->AddDevice(net0);
Names::Add("ES1#01", net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n1->AddDevice(net1);
Names::Add("ES2#01", net1);
Ptr<TsnNetDevice> net2 = CreateObject<TsnNetDevice>();
net2->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n2->AddDevice(net2);
Names::Add("ES3#01", net2);
Ptr<TsnNetDevice> net3_0 = CreateObject<TsnNetDevice>();
net3_0->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n3->AddDevice(net3_0);
Names::Add("SW1#01", net3_0);
Ptr<TsnNetDevice> net3_1 = CreateObject<TsnNetDevice>();
net3_1->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n3->AddDevice(net3_1);
Names::Add("SW1#02", net3_1);
Ptr<TsnNetDevice> net3_2 = CreateObject<TsnNetDevice>();
net3_2->SetAttribute("DataRate", DataRateValue(DataRate("1Gb/s")));
n3->AddDevice(net3_2);
Names::Add("SW1#03", net3_2);
//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(net3_0);
sw->AddSwitchPort(net3_1);
sw->AddSwitchPort(net3_2);
//Create Ethernet Channel and attach it two the netDevices
Ptr<EthernetChannel> l0 = CreateObject<EthernetChannel>();
l0->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net0->Attach(l0);
net3_0->Attach(l0);
Ptr<EthernetChannel> l1 = CreateObject<EthernetChannel>();
l1->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net1->Attach(l1);
net3_1->Attach(l1);
Ptr<EthernetChannel> l2 = CreateObject<EthernetChannel>();
l2->SetAttribute("Delay", TimeValue(Time(NanoSeconds(200))));
net2->Attach(l2);
net3_2->Attach(l2);
//Allocate a Mac address
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
net2->SetAddress(Mac48Address::Allocate());
sw->SetAddress(Mac48Address::Allocate());
net3_0->SetAddress(Mac48Address::Allocate());
net3_1->SetAddress(Mac48Address::Allocate());
net3_2->SetAddress(Mac48Address::Allocate());
//Add one fifo per netdevice.
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net2->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net3_2->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Add and configure GPTP
Ptr<GPTP> gPTP0 = CreateObject<GPTP>();
gPTP0->SetNode(n0);
gPTP0->SetMainClock(c0);
gPTP0->AddDomain(0);
gPTP0->AddPort(net0, GPTP::MASTER, 0);
gPTP0->SetAttribute("SyncInterval", TimeValue(Seconds(0.125))); //This line is not mandatory because 0.125s is the default value
gPTP0->SetAttribute("PdelayInterval", TimeValue(Seconds(1))); //This line is not mandatory because 1s is the default value
gPTP0->SetAttribute("Priority", UintegerValue(0));
n0->AddApplication(gPTP0);
gPTP0->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP1 = CreateObject<GPTP>();
gPTP1->SetNode(n1);
gPTP1->SetMainClock(c1);
gPTP1->AddDomain(0);
gPTP1->AddPort(net1, GPTP::SLAVE, 0);
gPTP1->SetAttribute("Priority", UintegerValue(0));
n1->AddApplication(gPTP1);
gPTP1->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP2 = CreateObject<GPTP>();
gPTP2->SetNode(n2);
gPTP2->SetMainClock(c2);
gPTP2->AddDomain(0);
gPTP2->AddPort(net2, GPTP::SLAVE, 0);
gPTP2->SetAttribute("Priority", UintegerValue(0));
n2->AddApplication(gPTP2);
gPTP2->SetStartTime(Seconds(0));
Ptr<GPTP> gPTP3 = CreateObject<GPTP>();
gPTP3->SetNode(n3);
gPTP3->SetMainClock(c3);
gPTP3->AddDomain(0);
gPTP3->AddPort(net3_0, GPTP::SLAVE, 0);
gPTP3->AddPort(net3_1, GPTP::MASTER, 0);
gPTP3->AddPort(net3_2, GPTP::MASTER, 0);
gPTP3->SetAttribute("Priority", UintegerValue(0));
n3->AddApplication(gPTP3);
gPTP3->SetStartTime(Seconds(0));
//Callback to displa information about GPTP execution
gPTP1->TraceConnectWithoutContext("Pdelay", MakeBoundCallback(&PdelayCallback, Names::FindName(n1)));
gPTP3->TraceConnectWithoutContext("Pdelay", MakeBoundCallback(&PdelayCallback, Names::FindName(n3)));
gPTP1->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n1), c0));
gPTP3->TraceConnectWithoutContext("ClockAfterCorrection", MakeBoundCallback(&ClockAfterCorrectionCallback, Names::FindName(n3), c0));
//Execute the simulation
Simulator::Stop(Seconds(3));
Simulator::Run();
Simulator::Destroy();
return 0;
}

View File

@@ -0,0 +1,8 @@
#include "tsn-helper.h"
namespace ns3
{
/* ... */
}

View File

@@ -0,0 +1,16 @@
#ifndef TSN_HELPER_H
#define TSN_HELPER_H
//#include "ns3/tsn.h"
namespace ns3
{
// Each class should be documented using Doxygen,
// and have an \ingroup tsn directive
/* ... */
}
#endif /* TSN_HELPER_H */

177
contrib/tsn/model/cbs.cc Normal file
View File

@@ -0,0 +1,177 @@
#include "cbs.h"
#include "ns3/tsn-transmission-selection-algo.h"
#include "ns3/log.h"
#include "ns3/nstime.h"
#include "ns3/uinteger.h"
#include "ns3/data-rate.h"
#include "ns3/simulator.h"
#include "ns3/traced-callback.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("Cbs");
NS_OBJECT_ENSURE_REGISTERED(Cbs);
TypeId
Cbs::GetTypeId()
{
static TypeId tid =
TypeId("ns3::Cbs")
.SetParent<TsnTransmissionSelectionAlgo>()
.SetGroupName("Tsn")
.AddConstructor<Cbs>()
.AddAttribute("IdleSlope",
"CBS idle slope parameter in bit/s",
DataRateValue(DataRate("1Gb/s")),
MakeDataRateAccessor(&Cbs::m_idleSlope),
MakeDataRateChecker())
.AddAttribute("portTransmitRate",
"CBS port transmit rate parameter in bit/s",
DataRateValue(DataRate("1Gb/s")),
MakeDataRateAccessor(&Cbs::m_portTransmitRate),
MakeDataRateChecker())
.AddTraceSource("Credit",
"CBS credit",
MakeTraceSourceAccessor(&Cbs::m_creditTrace),
"ns3::TracedValueCallback::double")
.AddAttribute("MaxIdleSlope",
"CBS idle slope parameter in bit/s",
DataRateValue(DataRate("1000Gb/s")),
MakeDataRateAccessor(&Cbs::m_maxIdleSlope),
MakeDataRateChecker());
return tid;
}
Cbs::Cbs()
{
NS_LOG_FUNCTION(this);
m_credit = 0;
m_state = IDLE;
m_transmissionGateIsOpen = true;
m_lastUpdateCreditTime = Simulator::Now();
}
Cbs::~Cbs()
{
NS_LOG_FUNCTION(this);
}
bool
Cbs::IsReadyToTransmit()
{
NS_LOG_FUNCTION(this);
if(m_credit>=0){
if (m_state==WAITING){
UpdateCredit(false);
}
return true;
}
else{
m_state = WAITING;
return false;
}
}
void
Cbs::UpdateCredit(bool trigger_cb)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_idleSlope < m_maxIdleSlope, "Trying to use an idleSlope higher than " << m_maxIdleSlope << ".");
Time sinceLastCreditUpdate = Simulator::Now() - m_lastUpdateCreditTime;
NS_LOG_INFO(sinceLastCreditUpdate);
NS_LOG_INFO("State = " << m_state);
if(m_transmissionGateIsOpen){
if(m_state == WAITING)
{
m_credit = m_credit + sinceLastCreditUpdate.GetSeconds() * m_idleSlope.GetBitRate();
}
else if(m_state == IDLE)
{
double newCredit = m_credit + sinceLastCreditUpdate.GetSeconds() * m_idleSlope.GetBitRate();
if(newCredit>=0)
{
m_creditTrace(m_credit); //Hit the trace before reseting for nicer plot
m_credit = 0;
}
else if(newCredit<0)
{
m_credit = newCredit;
}
}
}
m_creditTrace(m_credit);
m_lastUpdateCreditTime = Simulator::Now();
if(m_credit < 0 && m_transmissionGateIsOpen){
Time nextUdpateDelay = Time(Seconds((-m_credit/m_idleSlope.GetBitRate())));
NS_LOG_INFO("Now " << m_lastUpdateCreditTime.GetNanoSeconds());
NS_LOG_INFO("Next update in " << nextUdpateDelay.GetNanoSeconds());
NS_LOG_INFO("Credit : " << m_credit << "\n");
if(Time::GetResolution()==Time::NS and nextUdpateDelay.GetNanoSeconds()==0)
{
//To solve infinit event schedule due to rounding error
nextUdpateDelay = Time(NanoSeconds(1));
}
else if(Time::GetResolution()==Time::PS and nextUdpateDelay.GetPicoSeconds()==0)
{
//To solve infinit event schedule due to rounding error
nextUdpateDelay = Time(PicoSeconds(1));
}
Simulator::Cancel(m_NextCreditUpdate);
m_NextCreditUpdate = Simulator::Schedule(nextUdpateDelay, &Cbs::UpdateCredit, this, true);
}
if(trigger_cb && m_credit >= 0 && m_state==WAITING)
{
NS_ASSERT_MSG (m_net != nullptr, "NetDevice not set in CBS. Use cbs->SetTsnNetDevice(net);");
if (!m_MultidropMode)
{
ReadyToTransmitCallback();
}
}
}
void
Cbs::TransmitStart(Ptr<Packet> p, Time txTime)
{
NS_LOG_FUNCTION(this);
m_state = TRANSMITTING;
UpdateCredit(true);
}
void
Cbs::TransmitComplete(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
if (m_queue->IsEmpty()){
m_state = IDLE;
}
else{
m_state = WAITING;
}
int64_t sendSlope = m_idleSlope.GetBitRate() - m_portTransmitRate.GetBitRate();
Time sinceLastCreditUpdate = Simulator::Now() - m_lastUpdateCreditTime;
m_credit = m_credit + sendSlope*sinceLastCreditUpdate.GetSeconds();
m_lastUpdateCreditTime = Simulator::Now();
UpdateCredit(true);
}
void
Cbs::UpdateTransmissionGate(bool IsOpen)
{
NS_LOG_FUNCTION(this);
UpdateCredit(true);
m_transmissionGateIsOpen = IsOpen;
UpdateCredit(true);
if (not IsOpen)
{
Simulator::Cancel(m_NextCreditUpdate);
}
}
};

74
contrib/tsn/model/cbs.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef CBS_H
#define CBS_H
#include "ns3/tsn-transmission-selection-algo.h"
#include "ns3/data-rate.h"
#include "ns3/traced-callback.h"
namespace ns3
{
class Cbs: public TsnTransmissionSelectionAlgo
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a Cbs
*/
Cbs();
/**
* Destroy a Cbs
*
* This is the destructor for the Cbs.
*/
~Cbs();
// Delete copy constructor and assignment operator to avoid misuse
Cbs& operator=(const Cbs&) = delete;
Cbs(const Cbs&) = delete;
bool IsReadyToTransmit() override;
void TransmitStart(Ptr<Packet> p, Time txTime) override;
void TransmitComplete(Ptr<Packet> p) override;
void UpdateTransmissionGate(bool IsOpen) override;
TracedCallback<double> m_creditTrace;
protected:
private:
void UpdateCredit(bool trigger_cb);
DataRate m_idleSlope;
DataRate m_maxIdleSlope;
DataRate m_portTransmitRate;
double m_credit;
Time m_lastUpdateCreditTime;
EventId m_NextCreditUpdate;
enum State
{
/** Idle with no pkt in the FIFO */
IDLE,
/** Transmitting a packet */
TRANSMITTING,
/** Waiting with at least one pkt in the FIFO */
WAITING,
};
State m_state;
};
}
#endif /* CBS_H */

View File

@@ -0,0 +1,106 @@
#include "clock-constant-drift.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/uinteger.h"
#include <ns3/double.h>
#include "ns3/clock.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("ConstantDriftClock");
NS_OBJECT_ENSURE_REGISTERED(ConstantDriftClock);
TypeId
ConstantDriftClock::GetTypeId()
{
static TypeId tid =
TypeId("ns3::ConstantDriftClock")
.SetParent<Clock>()
.SetGroupName("tsn")
.AddConstructor<ConstantDriftClock>()
.AddAttribute("InitialOffset",
"Initial clock offset",
TimeValue(Time(0)),
MakeTimeAccessor(&ConstantDriftClock::m_initialOffset),
MakeTimeChecker())
.AddAttribute("DriftRate",
"Clock drift rate in ppm",
DoubleValue(0),
MakeDoubleAccessor(&ConstantDriftClock::m_driftRate),
MakeDoubleChecker<double>());
return tid;
}
ConstantDriftClock::ConstantDriftClock()
{
NS_LOG_FUNCTION(this);
}
ConstantDriftClock::~ConstantDriftClock()
{
NS_LOG_FUNCTION(this);
}
void
ConstantDriftClock::UpdateTime()
{
NS_LOG_FUNCTION(this);
if(!m_IsInitialised)
{
m_lastUpdateTimeValue = m_initialOffset;
m_IsInitialised = true;
}
Time durationSinceLastUpdate = Simulator::Now() - m_lastUpdateTime;
uint64_t t = (m_lastUpdateTimeValue + durationSinceLastUpdate + durationSinceLastUpdate * m_driftRate * pow(10,-6)).GetNanoSeconds();
Time tmp = Time(NanoSeconds( (t/m_granularity.GetNanoSeconds()) * m_granularity.GetNanoSeconds()));
if(m_lastUpdateTimeValue != tmp)
{
m_lastUpdateTimeValue = tmp;
m_lastUpdateTime = Simulator::Now();
}
}
Time
ConstantDriftClock::GetLocalTime()
{
NS_LOG_FUNCTION(this);
return GetUncorrectedLocalTime() + m_correctionOffset;
}
Time
ConstantDriftClock::GetUncorrectedLocalTime()
{
NS_LOG_FUNCTION(this);
UpdateTime();
return m_lastUpdateTimeValue;
}
Time
ConstantDriftClock::GetTimeAt(Time t)
{
NS_LOG_FUNCTION(this);
UpdateTime();
Time duration = t - m_lastUpdateTime;
uint64_t tmp = (m_lastUpdateTimeValue + duration + duration * m_driftRate * pow(10,-6)).GetNanoSeconds();
return m_correctionOffset + Time(NanoSeconds( (tmp/m_granularity.GetNanoSeconds()) * m_granularity.GetNanoSeconds()));
}
Time
ConstantDriftClock::GetDuration(Time d)
{
NS_LOG_FUNCTION(this);
uint64_t tmp = (d/(m_driftRate * pow(10,-6) + 1)).GetNanoSeconds();
return Time(NanoSeconds( (1 + tmp/m_granularity.GetNanoSeconds()) * m_granularity.GetNanoSeconds()));
}
}

View File

@@ -0,0 +1,56 @@
#ifndef CONSTANTDRIFTCLOCK_H
#define CONSTANTDRIFTCLOCK_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/clock.h"
namespace ns3
{
class ConstantDriftClock: public Clock
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a ConstantDriftClock
*/
ConstantDriftClock();
/**
* Destroy a ConstantDriftClock
*
* This is the destructor for the ConstantDriftClock.
*/
~ConstantDriftClock() override;
// Delete copy constructor and assignment operator to avoid misuse
ConstantDriftClock& operator=(const ConstantDriftClock&) = delete;
ConstantDriftClock(const ConstantDriftClock&) = delete;
Time GetLocalTime() override;
Time GetUncorrectedLocalTime() override;
Time GetTimeAt(Time t) override;
Time GetDuration(Time t) override;
void UpdateTime() override;
protected:
private:
Time m_initialOffset;
bool m_IsInitialised = false;
Time m_lastUpdateTime;
Time m_lastUpdateTimeValue;
double m_driftRate;
};
}
#endif /* CONSTANTDRIFTCLOCK_H */

View File

@@ -0,0 +1,83 @@
#include "clock-fix-precision.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/uinteger.h"
#include <ns3/double.h>
#include "ns3/clock.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("FixPrecisionClock");
NS_OBJECT_ENSURE_REGISTERED(FixPrecisionClock);
TypeId
FixPrecisionClock::GetTypeId()
{
static TypeId tid =
TypeId("ns3::FixPrecisionClock")
.SetParent<Clock>()
.SetGroupName("tsn")
.AddConstructor<FixPrecisionClock>()
.AddAttribute("Precision",
"Fix precision offset according to the reference clock (i.e. GM)",
TimeValue(Time(0)),
MakeTimeAccessor(&FixPrecisionClock::m_precision),
MakeTimeChecker());
return tid;
}
FixPrecisionClock::FixPrecisionClock()
{
NS_LOG_FUNCTION(this);
}
FixPrecisionClock::~FixPrecisionClock()
{
NS_LOG_FUNCTION(this);
}
void
FixPrecisionClock::SetRefClock(Ptr<Clock> refClock)
{
NS_LOG_FUNCTION(this);
m_refClock = refClock;
}
Time
FixPrecisionClock::GetLocalTime()
{
NS_LOG_FUNCTION(this);
return GetUncorrectedLocalTime() + m_precision;
}
Time
FixPrecisionClock::GetUncorrectedLocalTime()
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_refClock != nullptr,
"Reference clock no set using void VirtualClock::SetRefClock(Ptr<Clock> refClock)");
return m_refClock->GetUncorrectedLocalTime();
}
Time
FixPrecisionClock::GetTimeAt(Time t)
{
NS_LOG_FUNCTION(this);
return m_precision + m_refClock->GetTimeAt(t);
}
Time
FixPrecisionClock::GetDuration(Time d)
{
NS_LOG_FUNCTION(this);
return m_refClock->GetDuration(d);
}
}

View File

@@ -0,0 +1,64 @@
#ifndef FIXPRECISIONCLOCK_H
#define FIXPRECISIONCLOCK_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/clock.h"
namespace ns3
{
/**
* \ingroup tsn
* \brief Clock with fix precision.
*
* This class represents a non realistic clock that have a fix precision
* according to the grandmaster time base. It is only use to research purpose
* (search of the worst case scenario)
*
*/
class FixPrecisionClock: public Clock
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a FixPrecisionClock
*/
FixPrecisionClock();
/**
* Destroy a FixPrecisionClock
*
* This is the destructor for the FixPrecisionClock.
*/
~FixPrecisionClock() override;
// Delete copy constructor and assignment operator to avoid misuse
FixPrecisionClock& operator=(const FixPrecisionClock&) = delete;
FixPrecisionClock(const FixPrecisionClock&) = delete;
Time GetLocalTime() override;
Time GetUncorrectedLocalTime() override;
Time GetTimeAt(Time t) override;
Time GetDuration(Time t) override;
void SetRefClock(Ptr<Clock> refClock);
protected:
private:
Time m_precision;
Ptr<Clock> m_refClock = nullptr;
};
}
#endif /* FIXPRECISIONCLOCK_H */

View File

@@ -0,0 +1,77 @@
#include "clock-virtual.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/uinteger.h"
#include <ns3/double.h>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("VirtualClock");
NS_OBJECT_ENSURE_REGISTERED(VirtualClock);
TypeId
VirtualClock::GetTypeId()
{
static TypeId tid =
TypeId("ns3::VirtualClock")
.SetParent<Clock>()
.SetGroupName("tsn")
.AddConstructor<VirtualClock>();
return tid;
}
VirtualClock::VirtualClock()
{
NS_LOG_FUNCTION(this);
}
VirtualClock::~VirtualClock()
{
NS_LOG_FUNCTION(this);
}
void
VirtualClock::SetRefClock(Ptr<Clock> refClock)
{
NS_LOG_FUNCTION(this);
m_refClock = refClock;
}
Time
VirtualClock::GetLocalTime()
{
NS_LOG_FUNCTION(this);
return GetUncorrectedLocalTime() + m_correctionOffset;
}
Time
VirtualClock::GetUncorrectedLocalTime()
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_refClock != nullptr,
"Reference clock no set using void VirtualClock::SetRefClock(Ptr<Clock> refClock)");
return m_refClock->GetUncorrectedLocalTime();
}
Time
VirtualClock::GetTimeAt(Time t)
{
NS_LOG_FUNCTION(this);
return m_correctionOffset + m_refClock->GetTimeAt(t);
}
Time
VirtualClock::GetDuration(Time d)
{
NS_LOG_FUNCTION(this);
return m_refClock->GetDuration(d);
}
}

View File

@@ -0,0 +1,52 @@
#ifndef VIRTUALCLOCK_H
#define VIRTUALCLOCK_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/clock.h"
namespace ns3
{
class VirtualClock: public Clock
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a VirtualClock
*/
VirtualClock();
/**
* Destroy a VirtualClock
*
* This is the destructor for the VirtualClock.
*/
~VirtualClock() override;
// Delete copy constructor and assignment operator to avoid misuse
VirtualClock& operator=(const VirtualClock&) = delete;
VirtualClock(const VirtualClock&) = delete;
Time GetLocalTime() override;
Time GetUncorrectedLocalTime() override;
Time GetTimeAt(Time t) override;
Time GetDuration(Time t) override;
void SetRefClock(Ptr<Clock> refClock);
protected:
private:
Ptr<Clock> m_refClock = nullptr;
};
}
#endif /* VIRTUALCLOCK_H */

View File

@@ -0,0 +1,98 @@
#include "clock.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("Clock");
NS_OBJECT_ENSURE_REGISTERED(Clock);
TypeId
Clock::GetTypeId()
{
static TypeId tid =
TypeId("ns3::Clock")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<Clock>()
.AddAttribute("Granularity",
"Clock granularity in ns",
TimeValue(Time(NanoSeconds(1))),
MakeTimeAccessor(&Clock::m_granularity),
MakeTimeChecker());
return tid;
}
Clock::Clock()
{
NS_LOG_FUNCTION(this);
}
Clock::~Clock()
{
NS_LOG_FUNCTION(this);
}
Time
Clock::GetLocalTime()
{
NS_LOG_FUNCTION(this);
return GetUncorrectedLocalTime() + m_correctionOffset;
}
Time
Clock::GetUncorrectedLocalTime()
{
NS_LOG_FUNCTION(this);
return Simulator::Now();
}
Time
Clock::GetTimeAt(Time t)
{
NS_LOG_FUNCTION(this);
return t;
}
Time
Clock::GetDuration(Time d)
{
NS_LOG_FUNCTION(this);
return d;
}
void
Clock::UpdateTime(){
NS_LOG_FUNCTION(this);
}
void
Clock::SetOffsetTime(Time offset)
{
NS_LOG_FUNCTION(this);
m_correctionOffset = offset;
TriggerCorrectionCallback();
}
void
Clock::AddClockCorrectionCallback(Callback<void> cb)
{
NS_LOG_FUNCTION(this);
m_callbacks.insert(m_callbacks.end(), cb);
}
void
Clock::TriggerCorrectionCallback()
{
NS_LOG_FUNCTION(this);
for(int i = 0; i < (int)m_callbacks.size(); i++)
{
m_callbacks[i]();
}
}
}

70
contrib/tsn/model/clock.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef CLOCK_H
#define CLOCK_H
#include "ns3/object.h"
#include "ns3/nstime.h"
namespace ns3
{
class Clock: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a Clock
*/
Clock();
/**
* Destroy a Clock
*
* This is the destructor for the Clock.
*/
~Clock();
// Delete copy constructor and assignment operator to avoid misuse
Clock& operator=(const Clock&) = delete;
Clock(const Clock&) = delete;
void AddClockCorrectionCallback(Callback<void> cb);
void TriggerCorrectionCallback();
virtual Time GetLocalTime();
virtual Time GetUncorrectedLocalTime();
/**
*
* \brief Get Clock time at instant t in the simulator/perfect time base
*
* \params t Timestamp in the simulator/perfect time base
* \return clock time at instant t
*/
virtual Time GetTimeAt(Time t);
/**
*
* \brief Get duration in the clock timebase from duration in the simulator/perfect time base
*
* \params d duration in the simulator/perfect time base
* \return duration in the clock timebase
*/
virtual Time GetDuration(Time d);
virtual void UpdateTime();
void SetOffsetTime(Time offset);
protected:
Time m_correctionOffset = Time(0);
Time m_granularity;
private:
std::vector<Callback<void>> m_callbacks;
};
}
#endif /* CLOCK_H */

View File

@@ -0,0 +1,82 @@
#include "frer-base-recovery-function.h"
#include "ns3/log.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("BaseRecoveryFunction");
NS_OBJECT_ENSURE_REGISTERED(BaseRecoveryFunction);
TypeId
BaseRecoveryFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::BaseRecoveryFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<BaseRecoveryFunction>()
.AddAttribute("ResetTimer",
"Reset timer duration for recovery algorithm",
TimeValue(MilliSeconds(10000)),
MakeTimeAccessor(&BaseRecoveryFunction::m_frerSeqRcvyResetMSec),
MakeTimeChecker())
.AddAttribute("MinResetTimer",
"Min reset timer duration for recovery algorithm",
TimeValue(Seconds(0)),
MakeTimeAccessor(&BaseRecoveryFunction::m_frerMinSeqRcvyResetMSec),
MakeTimeChecker())
.AddAttribute("MaxResetTimer",
"Max reset timer duration for recovery algorithm",
TimeValue(Seconds(65535)),
MakeTimeAccessor(&BaseRecoveryFunction::m_frerMaxSeqRcvyResetMSec),
MakeTimeChecker());
return tid;
}
BaseRecoveryFunction::BaseRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
BaseRecoveryFunction::~BaseRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
uint
BaseRecoveryFunction::GetFrerCpsSeqRcvyPassedPackets()
{
NS_LOG_FUNCTION(this);
return m_frerCpsSeqRcvyPassedPackets;
}
uint
BaseRecoveryFunction::GetFrerCpsSeqRcvyDiscardedPackets()
{
NS_LOG_FUNCTION(this);
return m_frerCpsSeqRcvyDiscardedPackets;
}
bool
BaseRecoveryFunction::DoRecovery(uint16_t seqNumber)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqRcvyResetMSec >= m_frerMinSeqRcvyResetMSec, "Trying to use a shorter recovery reset timer than " << m_frerMinSeqRcvyResetMSec << ".");
NS_ASSERT_MSG(m_frerSeqRcvyResetMSec <= m_frerMaxSeqRcvyResetMSec, "Trying to use a longer recovery reset timer than " << m_frerMaxSeqRcvyResetMSec << ".");
return false;
}
void
BaseRecoveryFunction::resetRecoveryFunction()
{
NS_LOG_FUNCTION(this);
NS_LOG_INFO("Reset FRER recovery function !");
m_takeAny = true;
}
}

View File

@@ -0,0 +1,68 @@
#ifndef FRER_BASE_RECOVERY_FUNCTION_H
#define FRER_BASE_RECOVERY_FUNCTION_H
#include "ns3/object.h"
#include "ns3/nstime.h"
namespace ns3
{
class BaseRecoveryFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a BaseRecoveryFunction
*/
BaseRecoveryFunction();
/**
* Destroy a BaseRecoveryFunction
*
* This is the destructor for the BaseRecoveryFunction.
*/
~BaseRecoveryFunction();
// Delete copy constructor and assignment operator to avoid misuse
BaseRecoveryFunction& operator=(const BaseRecoveryFunction&) = delete;
BaseRecoveryFunction(const BaseRecoveryFunction&) = delete;
uint GetFrerCpsSeqRcvyPassedPackets();
uint GetFrerCpsSeqRcvyDiscardedPackets();
void resetRecoveryFunction();
virtual bool DoRecovery(uint16_t seqNumber);
protected:
uint m_recovSeqSpace = 65536; //802.1CB-2017 : 7.4.3.2.1
uint16_t m_recovSeqNum = 0; //802.1CB-2017 : 7.4.3.2.3
bool m_takeAny = true; //802.1CB-2017 : 7.4.3.2.6
Time m_frerSeqRcvyResetMSec; //802.1CB-2017 : 10.4.1.7
bool m_frerSeqRcvyIndividualRecovery = false; //802.1CB-2017 : 10.4.1.10
uint m_frerCpsSeqRcvyOutOfOrderPackets = 0; //802.1CB-2017 : 10.8.3
uint m_frerCpsSeqRcvyPassedPackets = 0; //802.1CB-2017 : 10.8.5
uint m_frerCpsSeqRcvyDiscardedPackets = 0; //802.1CB-2017 : 10.8.6
Time m_frerMinSeqRcvyResetMSec;
Time m_frerMaxSeqRcvyResetMSec;
EventId m_resetEvent;
private:
};
}
#endif /* FRER_BASE_RECOVERY_FUNCTION_H */

View File

@@ -0,0 +1,127 @@
#include "frer-latent-error-detection-function.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/uinteger.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("LatentErrorDetectionFunction");
NS_OBJECT_ENSURE_REGISTERED(LatentErrorDetectionFunction);
TypeId
LatentErrorDetectionFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::LatentErrorDetectionFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<LatentErrorDetectionFunction>()
.AddAttribute("TestTimer",
"Test timer duration",
TimeValue(Seconds(2)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyLatentErrorPeriod),
MakeTimeChecker())
.AddAttribute("ResetTimer",
"Reset timer duration",
TimeValue(Seconds(30)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyLatentResetPeriod),
MakeTimeChecker())
.AddAttribute("LatentErrorPaths",
"Number of Latent Error Paths",
UintegerValue(2),
MakeUintegerAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyLatentErrorPaths),
MakeUintegerChecker<uint>())
.AddAttribute("LatentErrorDifference",
"Latent Error Difference threshold",
UintegerValue(0),
MakeUintegerAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyLatentErrorDifference),
MakeUintegerChecker<uint>())
.AddAttribute("MinTestTimer",
"Min test timer duration",
TimeValue(Seconds(0)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyMinLatentErrorPeriod),
MakeTimeChecker())
.AddAttribute("MaxTestTimer",
"Max test timer duration",
TimeValue(Seconds(65535)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyMaxLatentErrorPeriod),
MakeTimeChecker())
.AddAttribute("MinResetTimer",
"Min reset timer duration",
TimeValue(Seconds(0)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyMinLatentResetPeriod),
MakeTimeChecker())
.AddAttribute("MaxResetTimer",
"Max reset timer duration",
TimeValue(Seconds(65535)),
MakeTimeAccessor(&LatentErrorDetectionFunction::m_frerSeqRcvyMaxLatentResetPeriod),
MakeTimeChecker());
return tid;
}
LatentErrorDetectionFunction::LatentErrorDetectionFunction()
{
NS_LOG_FUNCTION(this);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyLatentResetPeriod, &LatentErrorDetectionFunction::LatentErrorReset, this);
m_testEvent = Simulator::Schedule(m_frerSeqRcvyLatentErrorPeriod, &LatentErrorDetectionFunction::LatentErrorTest, this);
}
LatentErrorDetectionFunction::~LatentErrorDetectionFunction()
{
NS_LOG_FUNCTION(this);
}
void
LatentErrorDetectionFunction::SetRecoveryFunction(Ptr<BaseRecoveryFunction> recoveryFunction)
{
NS_LOG_FUNCTION(this);
m_recoveryFunction = recoveryFunction;
}
void
LatentErrorDetectionFunction::LatentErrorReset()
{
NS_LOG_FUNCTION(this);
// NS_LOG_INFO("Latent Error Reset ! ");
NS_ASSERT_MSG(m_recoveryFunction!=nullptr, "Recovery function not set ...");
m_curBaseDifference = (m_recoveryFunction->GetFrerCpsSeqRcvyPassedPackets() *(m_frerSeqRcvyLatentErrorPaths - 1)) - m_recoveryFunction->GetFrerCpsSeqRcvyDiscardedPackets();
m_frerCpsSeqRcvyLatentErrorResets += 1;
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyLatentResetPeriod, &LatentErrorDetectionFunction::LatentErrorReset, this);
}
void
LatentErrorDetectionFunction::LatentErrorTest()
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqRcvyLatentErrorPeriod >= m_frerSeqRcvyMinLatentErrorPeriod, "Trying to use a latent error test timer shorter than " << m_frerSeqRcvyMaxLatentErrorPeriod << ".");
NS_ASSERT_MSG(m_frerSeqRcvyLatentErrorPeriod <= m_frerSeqRcvyMaxLatentErrorPeriod, "Trying to use a latent error test timer longer than " << m_frerSeqRcvyMaxLatentErrorPeriod << ".");
NS_ASSERT_MSG(m_frerSeqRcvyLatentResetPeriod >= m_frerSeqRcvyMinLatentResetPeriod, "Trying to use a latent error reset timer shorter than " << m_frerSeqRcvyMinLatentResetPeriod << ".");
NS_ASSERT_MSG(m_frerSeqRcvyLatentResetPeriod <= m_frerSeqRcvyMaxLatentResetPeriod, "Trying to use a latent error reset timer longer than " << m_frerSeqRcvyMaxLatentResetPeriod << ".");
// NS_LOG_INFO("Latent Error Test ! ");
NS_ASSERT_MSG(m_recoveryFunction!=nullptr, "Recovery function not set ...");
int diff = m_curBaseDifference - ((m_recoveryFunction->GetFrerCpsSeqRcvyPassedPackets() *(m_frerSeqRcvyLatentErrorPaths - 1)) - m_recoveryFunction->GetFrerCpsSeqRcvyDiscardedPackets());
if (m_frerSeqRcvyLatentErrorPaths > 1 )
{
if (diff < 0)
{
diff = - diff;
}
if (diff > (int)m_frerSeqRcvyLatentErrorDifference)
{
NS_LOG_INFO("Latent Error Detected ! ");
//Hit a trace ?
}
}
m_testEvent = Simulator::Schedule(m_frerSeqRcvyLatentErrorPeriod, &LatentErrorDetectionFunction::LatentErrorTest, this);
}
}

View File

@@ -0,0 +1,71 @@
#ifndef FRER_LATENT_ERROR_DETECTION_FUNCTION_H
#define FRER_LATENT_ERROR_DETECTION_FUNCTION_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "frer-base-recovery-function.h"
namespace ns3
{
class LatentErrorDetectionFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a LatentErrorDetectionFunction
*/
LatentErrorDetectionFunction();
/**
* Destroy a LatentErrorDetectionFunction
*
* This is the destructor for the LatentErrorDetectionFunction.
*/
~LatentErrorDetectionFunction();
// Delete copy constructor and assignment operator to avoid misuse
LatentErrorDetectionFunction& operator=(const LatentErrorDetectionFunction&) = delete;
LatentErrorDetectionFunction(const LatentErrorDetectionFunction&) = delete;
void SetRecoveryFunction(Ptr<BaseRecoveryFunction> recoveryFunction);
void LatentErrorReset(); //802.1CB-2017 : 7.4.4.3
void LatentErrorTest(); //802.1CB-2017 : 7.4.4.4
protected:
Ptr<BaseRecoveryFunction> m_recoveryFunction = nullptr;
int m_curBaseDifference = 0; //802.1CB-2017 : 7.4.4.2.1
uint m_frerSeqRcvyLatentErrorDifference; //802.1CB-2017 : 10.4.1.12.1
Time m_frerSeqRcvyLatentErrorPeriod; //802.1CB-2017 : 10.4.1.12.2
Time m_frerSeqRcvyLatentResetPeriod; //802.1CB-2017 : 10.4.1.12.4
uint m_frerSeqRcvyLatentErrorPaths; //802.1CB-2017 : 10.4.1.12.3
uint m_frerCpsSeqRcvyLatentErrorResets = 0; //802.1CB-2017 : 10.8.10
Time m_frerSeqRcvyMinLatentErrorPeriod;
Time m_frerSeqRcvyMaxLatentErrorPeriod;
Time m_frerSeqRcvyMinLatentResetPeriod;
Time m_frerSeqRcvyMaxLatentResetPeriod;
EventId m_testEvent;
EventId m_resetEvent;
private:
};
}
#endif /* FRER_LATENT_ERROR_DETECTION_FUNCTION_H */

View File

@@ -0,0 +1,78 @@
#include "frer-match-recovery-function.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("MatchRecoveryFunction");
NS_OBJECT_ENSURE_REGISTERED(MatchRecoveryFunction);
TypeId
MatchRecoveryFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::MatchRecoveryFunction")
.SetParent<BaseRecoveryFunction>()
.SetGroupName("tsn")
.AddConstructor<MatchRecoveryFunction>();
return tid;
}
MatchRecoveryFunction::MatchRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
MatchRecoveryFunction::~MatchRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
bool
MatchRecoveryFunction::DoRecovery(uint16_t seqNumber)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqRcvyResetMSec <= m_frerMaxSeqRcvyResetMSec, "Trying to use a longer recovery reset timer than " << m_frerMaxSeqRcvyResetMSec << ".");
if(m_takeAny)
{
m_takeAny = false;
m_frerCpsSeqRcvyPassedPackets += 1;
m_recovSeqNum = seqNumber;
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
return true;
}
uint16_t delta = (seqNumber - m_recovSeqNum)%m_recovSeqSpace;
if (delta == 0)
{
//Pkt has been seen ... drop it
m_frerCpsSeqRcvyDiscardedPackets += 1;
if(m_frerSeqRcvyIndividualRecovery)
{
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
}
return false;
}
else
{
//Pkt hasn't been seen ... accept it
if (delta != 1)
{
//Pkt is out of order
m_frerCpsSeqRcvyOutOfOrderPackets += 1;
}
m_frerCpsSeqRcvyPassedPackets += 1;
m_recovSeqNum = seqNumber;
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More