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

@@ -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;
}
}
}

View File

@@ -0,0 +1,47 @@
#ifndef FRER_MATCH_RECOVERY_FUNCTION_H
#define FRER_MATCH_RECOVERY_FUNCTION_H
#include "ns3/frer-base-recovery-function.h"
namespace ns3
{
class MatchRecoveryFunction : public BaseRecoveryFunction
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a MatchRecoveryFunction
*/
MatchRecoveryFunction();
/**
* Destroy a MatchRecoveryFunction
*
* This is the destructor for the MatchRecoveryFunction.
*/
~MatchRecoveryFunction();
// Delete copy constructor and assignment operator to avoid misuse
MatchRecoveryFunction& operator=(const MatchRecoveryFunction&) = delete;
MatchRecoveryFunction(const MatchRecoveryFunction&) = delete;
bool DoRecovery(uint16_t seqNumber) override;
protected:
private:
};
}
#endif /* FRER_MATCH_RECOVERY_FUNCTION_H */

View File

@@ -0,0 +1,104 @@
#include "frer-sequence-encode-decode-function.h"
#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/ethernet-header2.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("SequenceEncodeDecodeFunction");
NS_OBJECT_ENSURE_REGISTERED(SequenceEncodeDecodeFunction);
TypeId
SequenceEncodeDecodeFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::SequenceEncodeDecodeFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<SequenceEncodeDecodeFunction>()
.AddAttribute("Direction",
"A Boolean object indicating whether the Sequence encode/decode function is to be placed on the out-facing (True) or in-facing (False) side of the port",
BooleanValue(false),
MakeBooleanAccessor(&SequenceEncodeDecodeFunction::m_frerSeqEncDirection),
MakeBooleanChecker())
.AddAttribute("Active",
"A Boolean value specifying whether this frerSeqEncEntry is passive (False) or active (True).",
BooleanValue(false),
MakeBooleanAccessor(&SequenceEncodeDecodeFunction::m_frerSeqEncActive),
MakeBooleanChecker());
return tid;
}
SequenceEncodeDecodeFunction::SequenceEncodeDecodeFunction()
{
NS_LOG_FUNCTION(this);
}
SequenceEncodeDecodeFunction::~SequenceEncodeDecodeFunction()
{
NS_LOG_FUNCTION(this);
}
void
SequenceEncodeDecodeFunction::SetStreamHandle(std::vector<uint32_t> seqEncStreamList)
{
NS_LOG_FUNCTION(this);
m_frerSeqEncStreamList = seqEncStreamList;
}
void
SequenceEncodeDecodeFunction::SetPort(Ptr<TsnNetDevice> seqEncPort)
{
NS_LOG_FUNCTION(this);
m_frerSeqEncPort = seqEncPort;
}
bool
SequenceEncodeDecodeFunction::IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction)
{
NS_LOG_FUNCTION(this);
if (m_frerSeqEncPort == port && m_frerSeqEncDirection == direction)
{
for (uint32_t i = 0; i < m_frerSeqEncStreamList.size(); i++)
{
if(m_frerSeqEncStreamList[i]==streamHandle)
{
return true;
}
}
}
return false;
}
void
SequenceEncodeDecodeFunction::Encode(Ptr<Packet> p, uint16_t seqNumber)
{
NS_LOG_FUNCTION(this);
if (m_frerSeqEncActive)
{
EthernetHeader2 header;
p->RemoveHeader(header);
header.SetRedundancyTag(seqNumber);
p->AddHeader(header);
}
}
uint16_t
SequenceEncodeDecodeFunction::Decode(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
EthernetHeader2 header;
p->RemoveHeader(header);
uint16_t seqNumber = header.RemoveRedundancyTag();
p->AddHeader(header);
return seqNumber;
}
}

View File

@@ -0,0 +1,56 @@
#ifndef FRER_SEQUENCE_ENCODE_DECODE_FUNCTION_H
#define FRER_SEQUENCE_ENCODE_DECODE_FUNCTION_H
#include "ns3/object.h"
#include "ns3/packet.h"
#include "tsn-net-device.h"
namespace ns3
{
class TsnNetDevice;
class SequenceEncodeDecodeFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a SequenceEncodeDecodeFunction
*/
SequenceEncodeDecodeFunction();
/**
* Destroy a SequenceEncodeDecodeFunction
*
* This is the destructor for the SequenceEncodeDecodeFunction.
*/
~SequenceEncodeDecodeFunction();
void SetStreamHandle(std::vector<uint32_t> seqEncStreamList);
void SetPort(Ptr<TsnNetDevice> seqEncPort);
bool IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction);
void Encode(Ptr<Packet> p, uint16_t seqNumber);
uint16_t Decode(Ptr<Packet> p);
protected:
private:
std::vector<uint32_t> m_frerSeqEncStreamList;
Ptr<TsnNetDevice> m_frerSeqEncPort;
bool m_frerSeqEncDirection;
bool m_frerSeqEncActive;
uint8_t m_frerSeqEncEncapsType = 1; //RTAG encoding (802.1CB-2017 section 10.5.1.5)
};
}
#endif /* FRER_SEQUENCE_ENCODE_DECODE_FUNCTION_H */

View File

@@ -0,0 +1,70 @@
#include "frer-sequence-generation-function.h"
#include "ns3/log.h"
#include "ns3/boolean.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("SequenceGenerationFunction");
NS_OBJECT_ENSURE_REGISTERED(SequenceGenerationFunction);
TypeId
SequenceGenerationFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::SequenceGenerationFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<SequenceGenerationFunction>()
.AddAttribute("Direction",
"A Boolean indicating whether the Sequence generation function is to be placed on the out-facing (True) or in-facing (False)",
BooleanValue(false),
MakeBooleanAccessor(&SequenceGenerationFunction::m_frerSeqGenDirection),
MakeBooleanChecker());
return tid;
}
SequenceGenerationFunction::SequenceGenerationFunction()
{
NS_LOG_FUNCTION(this);
}
SequenceGenerationFunction::~SequenceGenerationFunction()
{
NS_LOG_FUNCTION(this);
}
void
SequenceGenerationFunction::SetStreamHandle(std::vector<uint32_t> frerSeqGenStreamList)
{
NS_LOG_FUNCTION(this);
m_frerSeqGenStreamList = frerSeqGenStreamList;
}
bool
SequenceGenerationFunction::IsApplicable(uint32_t streamHandle)
{
NS_LOG_FUNCTION(this);
for (uint32_t i = 0; i < m_frerSeqGenStreamList.size(); i++)
{
if(m_frerSeqGenStreamList[i]==streamHandle)
{
return true;
}
}
return false;
}
uint16_t
SequenceGenerationFunction::GetSequenceNumber()
{
NS_LOG_FUNCTION(this);
uint16_t genSeqNum = m_GenSeqNum;
m_GenSeqNum++;
return genSeqNum;
}
}

View File

@@ -0,0 +1,54 @@
#ifndef FRER_SEQUENCE_GENERATION_FUNCTION_H
#define FRER_SEQUENCE_GENERATION_FUNCTION_H
#include "ns3/object.h"
namespace ns3
{
class SequenceGenerationFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a SequenceGenerationFunction
*/
SequenceGenerationFunction();
/**
* Destroy a SequenceGenerationFunction
*
* This is the destructor for the SequenceGenerationFunction.
*/
~SequenceGenerationFunction();
// Delete copy constructor and assignment operator to avoid misuse
SequenceGenerationFunction& operator=(const SequenceGenerationFunction&) = delete;
SequenceGenerationFunction(const SequenceGenerationFunction&) = delete;
void SetStreamHandle(std::vector<uint32_t> frerSeqGenStreamList);
bool IsApplicable(uint32_t streamHandle);
uint16_t GetSequenceNumber();
protected:
private:
std::vector<uint32_t> m_frerSeqGenStreamList;
bool m_frerSeqGenDirection; //out-facing (True) or in-facing (False)
uint16_t m_GenSeqNum = 0; //802.1CB-2017 7.4.1.2.2
};
}
#endif /* FRER_SEQUENCE_GENERATION_FUNCTION_H */

View File

@@ -0,0 +1,129 @@
#include "frer-sequence-recovery-function.h"
#include "ns3/log.h"
#include "ns3/boolean.h"
#include "ns3/random-variable-stream.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("SequenceRecoveryFunction");
NS_OBJECT_ENSURE_REGISTERED(SequenceRecoveryFunction);
TypeId
SequenceRecoveryFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::SequenceRecoveryFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<SequenceRecoveryFunction>()
.AddAttribute("Direction",
"A Boolean indicating whether the Sequence recovery or Individual recovery function is to be placed on the out-facing (True) or in-facing (False) side of the port.",
BooleanValue(false),
MakeBooleanAccessor(&SequenceRecoveryFunction::m_frerSeqRcvyDirection),
MakeBooleanChecker())
.AddAttribute("TakeNoSequence",
"A Boolean specifying whether packets with no sequence_number are to be accepted (True) or not (False).",
BooleanValue(false),
MakeBooleanAccessor(&SequenceRecoveryFunction::m_frerSeqRcvyTakeNoSequence),
MakeBooleanChecker())
.AddAttribute("IndividualRecovery",
"A Boolean specifying whether the function act as Individual recovery function (True) or Sequence recovery function (False)",
BooleanValue(false),
MakeBooleanAccessor(&SequenceRecoveryFunction::m_frerSeqRcvyIndividualRecovery),
MakeBooleanChecker())
.AddAttribute("MinLatencyOverhead",
"The minimum latency overhead cause by the recovery hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&SequenceRecoveryFunction::m_minLatencyOverhead),
MakeTimeChecker())
.AddAttribute("MaxLatencyOverhead",
"The maximun latency overhead cause by the recovery hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&SequenceRecoveryFunction::m_maxLatencyOverhead),
MakeTimeChecker());
return tid;
}
SequenceRecoveryFunction::SequenceRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
SequenceRecoveryFunction::~SequenceRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
void
SequenceRecoveryFunction::SetStreamHandle(std::vector<uint32_t> frerSeqRcvyStreamList)
{
NS_LOG_FUNCTION(this);
m_frerSeqRcvyStreamList = frerSeqRcvyStreamList;
}
void
SequenceRecoveryFunction::SetPorts(std::vector<Ptr<TsnNetDevice>> frerSeqRcvyPortList)
{
NS_LOG_FUNCTION(this);
m_frerSeqRcvyPortList = frerSeqRcvyPortList;
}
void
SequenceRecoveryFunction::SetRecoveryFunction(Ptr<BaseRecoveryFunction> frerSeqRcvyAlgorithm)
{
NS_LOG_FUNCTION(this);
m_frerSeqRcvyAlgorithm = frerSeqRcvyAlgorithm;
}
void
SequenceRecoveryFunction::SetLatentErrorDetectionFunction(Ptr<LatentErrorDetectionFunction> frerSeqRcvyLatentErrorDetection)
{
NS_LOG_FUNCTION(this);
m_frerSeqRcvyLatentErrorDetection = frerSeqRcvyLatentErrorDetection;
}
bool
SequenceRecoveryFunction::IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction, bool hasSeqNumber, bool individualRecovery)
{
NS_LOG_FUNCTION(this);
if (std::find(m_frerSeqRcvyStreamList.begin(), m_frerSeqRcvyStreamList.end(), streamHandle) != m_frerSeqRcvyStreamList.end())
{
if (std::find(m_frerSeqRcvyPortList.begin(), m_frerSeqRcvyPortList.end(), port) != m_frerSeqRcvyPortList.end())
{
if (m_frerSeqRcvyDirection == direction && m_frerSeqRcvyIndividualRecovery == individualRecovery)
{
if (!hasSeqNumber && m_frerSeqRcvyTakeNoSequence)
{
return true;
}
if(hasSeqNumber)
{
return true;
}
}
}
}
return false;
}
bool
SequenceRecoveryFunction::Recovery(uint16_t seqNumber)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqRcvyAlgorithm!=nullptr, "Recovery function not set ... Can be set using SetRecoveryFunction(Ptr<BaseRecoveryFunction> recoveryFunction)");
return m_frerSeqRcvyAlgorithm->DoRecovery(seqNumber);
}
Time
SequenceRecoveryFunction::GetHardwareLatency()
{
NS_LOG_FUNCTION(this);
Ptr<UniformRandomVariable> randVar = CreateObject<UniformRandomVariable>();
return NanoSeconds(randVar->GetValue(m_minLatencyOverhead.GetNanoSeconds(), m_maxLatencyOverhead.GetNanoSeconds()));
}
}

View File

@@ -0,0 +1,67 @@
#ifndef FRER_SEQUENCE_RECOVERY_FUNCTION_H
#define FRER_SEQUENCE_RECOVERY_FUNCTION_H
#include "ns3/object.h"
#include "tsn-net-device.h"
#include "frer-base-recovery-function.h"
#include "frer-latent-error-detection-function.h"
namespace ns3
{
class SequenceRecoveryFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a SequenceRecoveryFunction
*/
SequenceRecoveryFunction();
/**
* Destroy a SequenceRecoveryFunction
*
* This is the destructor for the SequenceRecoveryFunction.
*/
~SequenceRecoveryFunction();
// Delete copy constructor and assignment operator to avoid misuse
SequenceRecoveryFunction& operator=(const SequenceRecoveryFunction&) = delete;
SequenceRecoveryFunction(const SequenceRecoveryFunction&) = delete;
void SetStreamHandle(std::vector<uint32_t> frerSeqRcvyStreamList);
void SetPorts(std::vector<Ptr<TsnNetDevice>> frerSeqRcvyPortList);
void SetRecoveryFunction(Ptr<BaseRecoveryFunction> frerSeqRcvyAlgorithm);
void SetLatentErrorDetectionFunction(Ptr<LatentErrorDetectionFunction> frerSeqRcvyLatentErrorDetection);
bool IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction, bool hasSeqNumber, bool individualRecovery);
bool Recovery(uint16_t seqNumber);
Time GetHardwareLatency();
protected:
private:
std::vector<uint32_t> m_frerSeqRcvyStreamList; //802.1CB-2017 : 10.4.1.1
std::vector<Ptr<TsnNetDevice>> m_frerSeqRcvyPortList; //802.1CB-2017 : 10.4.1.2
bool m_frerSeqRcvyDirection; //802.1CB-2017 : 10.4.1.3
Ptr<BaseRecoveryFunction> m_frerSeqRcvyAlgorithm = nullptr; //802.1CB-2017 : 10.4.1.5
bool m_frerSeqRcvyTakeNoSequence; //802.1CB-2017 : 10.4.1.9
bool m_frerSeqRcvyIndividualRecovery; //802.1CB-2017 : 10.4.1.10
Ptr<LatentErrorDetectionFunction> m_frerSeqRcvyLatentErrorDetection = nullptr; //802.1CB-2017 : 10.4.1.11
Time m_minLatencyOverhead;
Time m_maxLatencyOverhead;
};
}
#endif /* FRER_SEQUENCE_RECOVERY_FUNCTION_H */

View File

@@ -0,0 +1,140 @@
#include "frer-vector-recovery-function.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/uinteger.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("VectorRecoveryFunction");
NS_OBJECT_ENSURE_REGISTERED(VectorRecoveryFunction);
TypeId
VectorRecoveryFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::VectorRecoveryFunction")
.SetParent<BaseRecoveryFunction>()
.SetGroupName("tsn")
.AddConstructor<VectorRecoveryFunction>()
.AddAttribute("HistoryLenght",
"Lenght of the history for the vector recovery algorithm",
UintegerValue(2),
MakeUintegerAccessor(&VectorRecoveryFunction::m_frerSeqRcvyHistoryLenght),
MakeUintegerChecker<uint>())
.AddAttribute("MaxHistoryLenght",
"Max Lenght of the history for the vector recovery algorithm",
UintegerValue(65535),
MakeUintegerAccessor(&VectorRecoveryFunction::m_maxFrerSeqRcvyHistoryLenght),
MakeUintegerChecker<uint16_t>());
return tid;
}
VectorRecoveryFunction::VectorRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
VectorRecoveryFunction::~VectorRecoveryFunction()
{
NS_LOG_FUNCTION(this);
}
bool
VectorRecoveryFunction::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 << ".");
NS_ASSERT_MSG(m_frerSeqRcvyHistoryLenght <= m_maxFrerSeqRcvyHistoryLenght, "Trying to use a longer HistoryLenght than " << m_maxFrerSeqRcvyHistoryLenght << ".");
// Compute signed difference modulo RecovSeqSpace
int delta = (seqNumber-m_recovSeqNum) % m_recovSeqSpace;
if (0 != (delta & (m_recovSeqSpace/2)))
{
delta = delta - m_recovSeqSpace;
// Here, -(m_recovSeqSpace/2)<=delta<=((m_recovSeqSpace/2)-1)
}
// After reset, accept any packet
if(m_takeAny)
{
m_takeAny = false;
m_sequenceHistory.clear(); //Empty the vector in case of reset
m_sequenceHistory.insert(m_sequenceHistory.begin(), true); //Add a "seen" bool
for(int i = 0; i<m_frerSeqRcvyHistoryLenght-1; i++)
{
m_sequenceHistory.insert(m_sequenceHistory.end(), false); //Fill the others bool
}
m_recovSeqNum = seqNumber;
m_frerCpsSeqRcvyPassedPackets += 1;
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
return true;
}
else if (delta >= m_frerSeqRcvyHistoryLenght || delta <= -m_frerSeqRcvyHistoryLenght)
{
// Packet is out-of-range. Count and discard it.
m_frerCpsSeqRcvyRoguePackets += 1;
m_frerCpsSeqRcvyDiscardedPackets += 1;
if(m_frerSeqRcvyIndividualRecovery)
{
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
}
return false;
}
else if (delta <= 0)
{
// Packet is old and in SequenceHistory; have we seen it before?
if (not m_sequenceHistory[-delta])
{
// Packet has not been seen. Take it.
m_sequenceHistory[-delta] = true;
m_frerCpsSeqRcvyOutOfOrderPackets += 1;
m_frerCpsSeqRcvyPassedPackets += 1;
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
return true;
}
else
{
// Packet 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
{
// Packet is not too far ahead of the one we want.
// Packet is out-of-order unless it directly follows RecovSeqNum
if (delta != 1)
{
m_frerCpsSeqRcvyOutOfOrderPackets += 1;
}
// Shift the history until bit 0 refers to sequence_number.
while (0 != (delta = delta - 1)){
m_sequenceHistory.insert(m_sequenceHistory.begin(), false);
}
m_sequenceHistory.insert(m_sequenceHistory.begin(), true);
m_sequenceHistory.resize(m_frerSeqRcvyHistoryLenght);
m_recovSeqNum = seqNumber;
m_frerCpsSeqRcvyPassedPackets += 1;
Simulator::Cancel(m_resetEvent);
m_resetEvent = Simulator::Schedule(m_frerSeqRcvyResetMSec, &BaseRecoveryFunction::resetRecoveryFunction, this);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,51 @@
#ifndef FRER_VECTOR_RECOVERY_FUNCTION_H
#define FRER_VECTOR_RECOVERY_FUNCTION_H
#include "ns3/frer-base-recovery-function.h"
namespace ns3
{
class VectorRecoveryFunction : public BaseRecoveryFunction
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a VectorRecoveryFunction
*/
VectorRecoveryFunction();
/**
* Destroy a VectorRecoveryFunction
*
* This is the destructor for the VectorRecoveryFunction.
*/
~VectorRecoveryFunction();
// Delete copy constructor and assignment operator to avoid misuse
VectorRecoveryFunction& operator=(const VectorRecoveryFunction&) = delete;
VectorRecoveryFunction(const VectorRecoveryFunction&) = delete;
bool DoRecovery(uint16_t seqNumber) override;
protected:
private:
std::vector<bool> m_sequenceHistory; //802.1CB-2017 : 7.4.3.2.2
int m_frerSeqRcvyHistoryLenght; //802.1CB-2017 : 10.4.1.6
uint m_frerCpsSeqRcvyRoguePackets = 0; //802.1CB-2017 : 10.8.4
uint16_t m_maxFrerSeqRcvyHistoryLenght;
};
}
#endif /* FRER_VECTOR_RECOVERY_FUNCTION_H */

View File

@@ -0,0 +1,282 @@
#include "gPTP-header.h"
#include "ns3/buffer.h"
#include "ns3/abort.h"
#include "ns3/assert.h"
#include "ns3/header.h"
#include "ns3/log.h"
#include <iomanip>
#include <iostream>
#include <bitset>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("GPTPHeader");
NS_OBJECT_ENSURE_REGISTERED(GptpHeader);
GptpHeader::GptpHeader()
{
NS_LOG_FUNCTION_NOARGS();
}
GptpHeader::~GptpHeader()
{
NS_LOG_FUNCTION_NOARGS();
}
TypeId
GptpHeader::GetTypeId()
{
NS_LOG_FUNCTION_NOARGS();
static TypeId tid = TypeId("ns3::GptpHeader")
.SetParent<Header>()
.SetGroupName("Tsn")
.AddConstructor<GptpHeader>();
return tid;
}
TypeId
GptpHeader::GetInstanceTypeId() const
{
NS_LOG_FUNCTION_NOARGS();
return GetTypeId();
}
void
GptpHeader::Print(std::ostream& os) const
{
NS_LOG_FUNCTION(this);
std::string proto;
os <<"GPTP print ==> TODO";
}
uint32_t
GptpHeader::GetSerializedSize() const
{
NS_LOG_FUNCTION_NOARGS();
return 34;
}
void
GptpHeader::Serialize(Buffer::Iterator start) const
{
NS_LOG_FUNCTION(this);
uint8_t flag1 = ((0 << 7) |
(m_ptpProfifilSpecific2Flag << 6) |
(m_ptpProfifilSpecific1Flag << 5) |
(0 << 4) |
(0 << 3) |
(m_unicastFlag << 2) |
(m_twoStepFlag << 1) |
(m_alternateMasterFlag << 0));
uint8_t flag2 = ((0 << 7) |
(0 << 6) |
(m_frequencyTraceable << 5) |
(m_timeTraceable << 4) |
(m_ptpTimescale << 3) |
(m_currentUtcOffsetValid << 2) |
(m_leap59 << 1) |
(m_leap61 << 0));
Buffer::Iterator i = start;
i.WriteU8((m_majorSdoId << 4) | m_messageType);
i.WriteU8((m_minorVersionPTP << 4) | m_versionPTP);
i.WriteHtonU16(m_messageLenght);
i.WriteU8(m_domainNumber);
i.WriteU8(m_minorSdoId);
i.WriteU8(flag1);
i.WriteU8(flag2);
i.WriteHtonU64(m_correctionField);
i.WriteHtonU32(m_messageTypeSpecific);
i.WriteHtonU64(m_clockIndentity);
i.WriteHtonU16(m_portIdentity);
i.WriteHtonU16(m_sequenceId);
i.WriteU8(m_controlField);
i.WriteU8(m_logMessageInterval);
// 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
GptpHeader::Deserialize(Buffer::Iterator start)
{
NS_LOG_FUNCTION(this);
Buffer::Iterator i = start;
uint8_t tmp = i.ReadU8();
m_majorSdoId = (tmp & 0b11110000) >> 4;
m_messageType = tmp & 0b00001111;
tmp = i.ReadU8();
m_minorVersionPTP = (tmp & 0b11110000) >> 4;
m_versionPTP = tmp & 0b00001111;
m_messageLenght = i.ReadNtohU16();
m_domainNumber = i.ReadU8();
m_minorSdoId = i.ReadU8();
uint16_t flags = i.ReadNtohU16();
m_alternateMasterFlag = (flags & 0b0000000100000000) >> 8;
m_twoStepFlag = (flags & 0b0000001000000000) >> 9;
m_unicastFlag = (flags & 0b0000010000000000) >> 10;
m_ptpProfifilSpecific1Flag = (flags & 0b0010000000000000) >> 13;
m_ptpProfifilSpecific2Flag = (flags & 0b0100000000000000) >> 14;
m_leap61 = (flags & 0b0000000000000001) >> 0;
m_leap59 = (flags & 0b0000000000000010) >> 1;
m_currentUtcOffsetValid = (flags & 0b0000000000000100) >> 2;
m_ptpTimescale = (flags & 0b0000000000001000) >> 3;
m_timeTraceable = (flags & 0b0000000000010000) >> 4;
m_frequencyTraceable = (flags & 0b0000000000100000) >> 5;
m_correctionField = i.ReadNtohU64();
m_messageTypeSpecific = i.ReadNtohU32();
m_clockIndentity = i.ReadNtohU64();
m_portIdentity = i.ReadNtohU16();
m_sequenceId = i.ReadNtohU16();
m_controlField = i.ReadU8();
m_logMessageInterval = i.ReadU8();
// NS_LOG_INFO("m_majorSdoId " << (uint16_t)m_majorSdoId);
// NS_LOG_INFO("m_messageType " << (uint16_t)m_messageType);
// NS_LOG_INFO("m_minorVersionPTP " << (uint16_t)m_minorVersionPTP);
// NS_LOG_INFO("m_versionPTP " << (uint16_t)m_versionPTP);
// NS_LOG_INFO("m_messageLenght " << (uint16_t)m_messageLenght);
// NS_LOG_INFO("m_domainNumber " << (uint16_t)m_domainNumber);
// NS_LOG_INFO("m_minorSdoId " << (uint16_t)m_minorSdoId);
// NS_LOG_INFO("m_alternateMasterFlag " << (uint16_t)m_alternateMasterFlag);
// NS_LOG_INFO("m_twoStepFlag " << (uint16_t)m_twoStepFlag);
// NS_LOG_INFO("m_unicastFlag " << (uint16_t)m_unicastFlag);
// NS_LOG_INFO("m_ptpProfifilSpecific1Flag " << (uint16_t)m_ptpProfifilSpecific1Flag);
// NS_LOG_INFO("m_ptpProfifilSpecific2Flag " << (uint16_t)m_ptpProfifilSpecific2Flag);
// NS_LOG_INFO("m_leap61 " << (uint16_t)m_leap61);
// NS_LOG_INFO("m_leap59 " << (uint16_t)m_leap59);
// NS_LOG_INFO("m_currentUtcOffsetValid " << (uint16_t)m_currentUtcOffsetValid);
// NS_LOG_INFO("m_ptpTimescale " << (uint16_t)m_ptpTimescale);
// NS_LOG_INFO("m_timeTraceable " << (uint16_t)m_timeTraceable);
// NS_LOG_INFO("m_frequencyTraceable " << (uint16_t)m_frequencyTraceable);
// NS_LOG_INFO("m_correctionField " << m_correctionField);
// NS_LOG_INFO("m_clockIndentity " << m_clockIndentity);
// NS_LOG_INFO("m_portIdentity " << m_portIdentity);
// NS_LOG_INFO("m_sequenceId " << m_sequenceId);
// NS_LOG_INFO("m_controlField " << (uint16_t)m_controlField);
// NS_LOG_INFO("m_logMessageInterval " << (uint16_t)m_logMessageInterval);
return GetSerializedSize();
}
void
GptpHeader::SetMessageType(uint8_t messageType)
{
m_messageType = messageType;
}
void
GptpHeader::SetMessageLenght(uint8_t messageLenght)
{
m_messageLenght = messageLenght;
}
void
GptpHeader::SetDomainNumber(uint8_t domainNumber)
{
m_domainNumber = domainNumber;
}
void
GptpHeader::SetTwoStepFlag(bool twoStepFlag)
{
m_twoStepFlag = twoStepFlag;
}
void
GptpHeader::SetCorrectionField(uint64_t correctionField)
{
m_correctionField = correctionField;
}
void
GptpHeader::SetClockIdentity(uint64_t clockIdentity)
{
m_clockIndentity = clockIdentity;
}
void
GptpHeader::SetPortIdentity(uint16_t portIdentity)
{
m_portIdentity = portIdentity;
}
void
GptpHeader::SetSequenceId(uint16_t sequenceId)
{
m_sequenceId = sequenceId;
}
void
GptpHeader::SetLogMessageInterval(uint8_t logMessageInterval)
{
m_logMessageInterval = logMessageInterval;
}
uint8_t
GptpHeader::GetMessageType()
{
return m_messageType;
}
uint8_t
GptpHeader::GetDomainNumber()
{
return m_domainNumber;
}
bool
GptpHeader::GetTwoStepFlag()
{
return m_twoStepFlag;
}
uint64_t
GptpHeader::GetCorrectionField()
{
return m_correctionField;
}
uint64_t
GptpHeader::GetClockIdentity()
{
return m_clockIndentity;
}
uint16_t
GptpHeader::GetPortIdentity()
{
return m_portIdentity;
}
uint16_t
GptpHeader::GetSequenceId()
{
return m_sequenceId;
}
uint8_t
GptpHeader::GetLogMessageInterval()
{
return m_logMessageInterval;
}
}

View File

@@ -0,0 +1,104 @@
#ifndef GPTP_HEADER_H
#define GPTP_HEADER_H
#include "ns3/header.h"
namespace ns3
{
/**
* \ingroup ethernet
* \brief Packet header for Ethernet
*
* This class can be used to add a header to Ethernet packet.
*/
class GptpHeader : public Header
{
public:
/**
* \brief Construct a gPTP header.
*/
GptpHeader();
/**
* \brief Destroy a gPTP header.
*/
~GptpHeader() 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 SetMessageType(uint8_t messageType);
void SetMessageLenght(uint8_t messageLenght);
void SetDomainNumber(uint8_t domainNumber);
void SetTwoStepFlag(bool twoStepFlag);
void SetCorrectionField(uint64_t correctionField);
void SetClockIdentity(uint64_t clockIdentity);
void SetPortIdentity(uint16_t portIdentity);
void SetSequenceId(uint16_t sequenceId);
void SetLogMessageInterval(uint8_t logMessageInterval);
uint8_t GetMessageType();
uint8_t GetDomainNumber();
bool GetTwoStepFlag();
uint64_t GetCorrectionField();
uint64_t GetClockIdentity();
uint16_t GetPortIdentity();
uint16_t GetSequenceId();
uint8_t GetLogMessageInterval();
private:
//STD 802.1AS-2020 : Talbe 10-7 - PTP message header
uint8_t m_majorSdoId = 1;
uint8_t m_messageType = 0;
uint8_t m_minorVersionPTP = 0;
uint8_t m_versionPTP = 2;
uint16_t m_messageLenght = 0;
uint8_t m_domainNumber = 0;
uint8_t m_minorSdoId = 0;
//Flags :
bool m_alternateMasterFlag = false;
bool m_twoStepFlag = false;
bool m_unicastFlag = false;
bool m_ptpProfifilSpecific1Flag = false;
bool m_ptpProfifilSpecific2Flag = false;
bool m_leap61 = false;
bool m_leap59 = false;
bool m_currentUtcOffsetValid = false;
bool m_ptpTimescale = false;
bool m_timeTraceable = false;
bool m_frequencyTraceable = false;
uint64_t m_correctionField = 0;
uint32_t m_messageTypeSpecific = 0;
//SourceClockIdentity
uint64_t m_clockIndentity = 0;
uint16_t m_portIdentity = 0;
uint16_t m_sequenceId = 0;
uint8_t m_controlField = 0;
uint8_t m_logMessageInterval = 0;
};
}
#endif /* GPTP_HEADER_H */

View File

@@ -0,0 +1,232 @@
#include "gPTP-packet.h"
#include "ns3/log.h"
#include "ns3/object.h"
#include "ns3/packet.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("GPTPPacket");
NS_OBJECT_ENSURE_REGISTERED(PdelayPayload);
TypeId
PdelayPayload::GetTypeId()
{
static TypeId tid =
TypeId("ns3::PdelayPayload")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<PdelayPayload>();
return tid;
}
PdelayPayload::PdelayPayload()
{
NS_LOG_FUNCTION(this);
m_pkt = Create<Packet>(20);
}
PdelayPayload::PdelayPayload(uint64_t timestampSecond, uint32_t timestampNanoSecond, uint64_t clockIdentity, uint16_t portIdentity)
{
NS_LOG_FUNCTION(this);
uint8_t txBuffer[20] = {};
txBuffer[0] = (timestampSecond & 0xFF0000000000) >> 40;
txBuffer[1] = (timestampSecond & 0x00FF00000000) >> 32;
txBuffer[2] = (timestampSecond & 0x0000FF000000) >> 24;
txBuffer[3] = (timestampSecond & 0x000000FF0000) >> 16;
txBuffer[4] = (timestampSecond & 0x00000000FF00) >> 8;
txBuffer[5] = (timestampSecond & 0x0000000000FF);
txBuffer[6] = (timestampNanoSecond & 0xFF000000) >> 24;
txBuffer[7] = (timestampNanoSecond & 0x00FF0000) >> 16;
txBuffer[8] = (timestampNanoSecond & 0x0000FF00) >> 8;
txBuffer[9] = (timestampNanoSecond & 0x000000FF);
txBuffer[10] = (clockIdentity & 0xFF00000000000000) >> 56;
txBuffer[11] = (clockIdentity & 0x00FF000000000000) >> 48;
txBuffer[12] = (clockIdentity & 0x0000FF0000000000) >> 40;
txBuffer[13] = (clockIdentity & 0x000000FF00000000) >> 32;
txBuffer[14] = (clockIdentity & 0x00000000FF000000) >> 24;
txBuffer[15] = (clockIdentity & 0x0000000000FF0000) >> 16;
txBuffer[16] = (clockIdentity & 0x000000000000FF00) >> 8;
txBuffer[17] = (clockIdentity & 0x00000000000000FF);
txBuffer[18] = (portIdentity & 0xFF00) >> 8;
txBuffer[19] = (portIdentity & 0x00FF);
size_t txBufferSize = sizeof(txBuffer);
m_pkt = Create<Packet>(txBuffer, txBufferSize);
}
PdelayPayload::~PdelayPayload()
{
NS_LOG_FUNCTION(this);
}
Ptr<Packet>
PdelayPayload::GetPkt()
{
NS_LOG_FUNCTION(this);
return m_pkt;
}
NS_OBJECT_ENSURE_REGISTERED(SyncPayload);
TypeId
SyncPayload::GetTypeId()
{
static TypeId tid =
TypeId("ns3::SyncPayload")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<SyncPayload>();
return tid;
}
SyncPayload::SyncPayload()
{
NS_LOG_FUNCTION(this);
m_pkt = Create<Packet>(10);
}
SyncPayload::SyncPayload(uint64_t timestampSecond, uint32_t timestampNanoSecond)
{
NS_LOG_FUNCTION(this);
uint8_t txBuffer[10] = {};
txBuffer[0] = (timestampSecond & 0xFF0000000000) >> 40;
txBuffer[1] = (timestampSecond & 0x00FF00000000) >> 32;
txBuffer[2] = (timestampSecond & 0x0000FF000000) >> 24;
txBuffer[3] = (timestampSecond & 0x000000FF0000) >> 16;
txBuffer[4] = (timestampSecond & 0x00000000FF00) >> 8;
txBuffer[5] = (timestampSecond & 0x0000000000FF);
txBuffer[6] = (timestampNanoSecond & 0xFF000000) >> 24;
txBuffer[7] = (timestampNanoSecond & 0x00FF0000) >> 16;
txBuffer[8] = (timestampNanoSecond & 0x0000FF00) >> 8;
txBuffer[9] = (timestampNanoSecond & 0x000000FF);
size_t txBufferSize = sizeof(txBuffer);
m_pkt = Create<Packet>(txBuffer, txBufferSize);
}
SyncPayload::~SyncPayload()
{
NS_LOG_FUNCTION(this);
}
Ptr<Packet>
SyncPayload::GetPkt()
{
NS_LOG_FUNCTION(this);
return m_pkt;
}
NS_OBJECT_ENSURE_REGISTERED(FollowUpPayload);
TypeId
FollowUpPayload::GetTypeId()
{
static TypeId tid =
TypeId("ns3::FollowUpPayload")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<FollowUpPayload>();
return tid;
}
FollowUpPayload::FollowUpPayload()
{
NS_LOG_FUNCTION(this);
m_pkt = Create<Packet>(42);
}
FollowUpPayload::FollowUpPayload(uint64_t timestampSecond,
uint32_t timestampNanoSecond,
uint32_t cumulativeScaledRateRatio,
uint16_t gmTimeBaseIndicator,
uint32_t lastGmPhaseChange0,
uint32_t lastGmPhaseChange1,
uint32_t lastGmPhaseChange2,
uint32_t scaledLastGmFreqChange)
{
NS_LOG_FUNCTION(this);
uint8_t txBuffer[42] = {};
txBuffer[0] = (timestampSecond & 0xFF0000000000) >> 40;
txBuffer[1] = (timestampSecond & 0x00FF00000000) >> 32;
txBuffer[2] = (timestampSecond & 0x0000FF000000) >> 24;
txBuffer[3] = (timestampSecond & 0x000000FF0000) >> 16;
txBuffer[4] = (timestampSecond & 0x00000000FF00) >> 8;
txBuffer[5] = (timestampSecond & 0x0000000000FF);
txBuffer[6] = (timestampNanoSecond & 0xFF000000) >> 24;
txBuffer[7] = (timestampNanoSecond & 0x00FF0000) >> 16;
txBuffer[8] = (timestampNanoSecond & 0x0000FF00) >> 8;
txBuffer[9] = (timestampNanoSecond & 0x000000FF);
txBuffer[10] = (m_tlvType & 0xFF00);
txBuffer[11] = (m_tlvType & 0x00FF);
txBuffer[12] = (m_lenghtField & 0xFF00);
txBuffer[13] = (m_lenghtField & 0x00FF);
txBuffer[14] = (m_organizationId & 0xFF0000);
txBuffer[15] = (m_organizationId & 0x00FF00);
txBuffer[16] = (m_organizationId & 0x0000FF);
txBuffer[17] = (m_organizationSubType & 0xFF0000);
txBuffer[18] = (m_organizationSubType & 0x00FF00);
txBuffer[19] = (m_organizationSubType & 0x0000FF);
txBuffer[20] = (cumulativeScaledRateRatio & 0xFF000000);
txBuffer[21] = (cumulativeScaledRateRatio & 0x00FF0000);
txBuffer[22] = (cumulativeScaledRateRatio & 0x0000FF00);
txBuffer[23] = (cumulativeScaledRateRatio & 0x000000FF);
txBuffer[24] = (gmTimeBaseIndicator & 0xFF00);
txBuffer[25] = (gmTimeBaseIndicator & 0x00FF);
txBuffer[26] = (lastGmPhaseChange0 & 0xFF000000);
txBuffer[27] = (lastGmPhaseChange0 & 0x00FF0000);
txBuffer[28] = (lastGmPhaseChange0 & 0x0000FF00);
txBuffer[29] = (lastGmPhaseChange0 & 0x000000FF);
txBuffer[30] = (lastGmPhaseChange1 & 0xFF000000);
txBuffer[31] = (lastGmPhaseChange1 & 0x00FF0000);
txBuffer[32] = (lastGmPhaseChange1 & 0x0000FF00);
txBuffer[33] = (lastGmPhaseChange1 & 0x000000FF);
txBuffer[34] = (lastGmPhaseChange2 & 0xFF000000);
txBuffer[35] = (lastGmPhaseChange2 & 0x00FF0000);
txBuffer[36] = (lastGmPhaseChange2 & 0x0000FF00);
txBuffer[37] = (lastGmPhaseChange2 & 0x000000FF);
txBuffer[38] = (scaledLastGmFreqChange & 0xFF000000);
txBuffer[39] = (scaledLastGmFreqChange & 0x00FF0000);
txBuffer[40] = (scaledLastGmFreqChange & 0x0000FF00);
txBuffer[41] = (scaledLastGmFreqChange & 0x000000FF);
size_t txBufferSize = sizeof(txBuffer);
m_pkt = Create<Packet>(txBuffer, txBufferSize);
}
FollowUpPayload::~FollowUpPayload()
{
NS_LOG_FUNCTION(this);
}
Ptr<Packet>
FollowUpPayload::GetPkt()
{
NS_LOG_FUNCTION(this);
return m_pkt;
}
}

View File

@@ -0,0 +1,121 @@
#ifndef GPTP_PACKET_H
#define GPTP_PACKET_H
#include "ns3/object.h"
#include "ns3/packet.h"
namespace ns3
{
class PdelayPayload : public Object
{
public :
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a PdelayPayload
*/
PdelayPayload();
PdelayPayload(uint64_t timestampSecond, uint32_t timestampNanoSecond, uint64_t clockIdentity, uint16_t portIdentity);
/**
* Destroy a PdelayPacket
*
* This is the destructor for the PdelayPayload.
*/
~PdelayPayload();
Ptr<Packet> GetPkt();
private :
Ptr<Packet> m_pkt = nullptr;
};
class SyncPayload : public Object
{
public :
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a SyncPayload
*/
SyncPayload();
SyncPayload(uint64_t timestampSecond, uint32_t timestampNanoSecond);
/**
* Destroy a SyncPayload
*
* This is the destructor for the SyncPayload.
*/
~SyncPayload();
Ptr<Packet> GetPkt();
private :
Ptr<Packet> m_pkt = nullptr;
};
class FollowUpPayload : public Object
{
public :
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a FollowUpPayload
*/
FollowUpPayload();
FollowUpPayload(uint64_t timestampSecond,
uint32_t timestampNanoSecond,
uint32_t cumulativeScaledRateRatio,
uint16_t gmTimeBaseIndicator,
uint32_t lastGmPhaseChange0,
uint32_t lastGmPhaseChange1,
uint32_t lastGmPhaseChange2,
uint32_t scaledLastGmFreqChange);
/**
* Destroy a FollowUpPayload
*
* This is the destructor for the SyncPayload.
*/
~FollowUpPayload();
Ptr<Packet> GetPkt();
private :
Ptr<Packet> m_pkt = nullptr;
uint16_t m_tlvType = 0x3; //STD 802.1AS-2020 : 11.4.4.3.2
uint16_t m_lenghtField = 28; //STD 802.1AS-2020 : 11.4.4.3.3
uint32_t m_organizationId = 0x0080C2; //STD 802.1AS-2020 : 11.4.4.3.4
uint32_t m_organizationSubType = 1; //STD 802.1AS-2020 : 11.4.4.3.5
};
}
#endif /* GPTP_PACKET_H */

771
contrib/tsn/model/gPTP.cc Normal file
View File

@@ -0,0 +1,771 @@
#include "gPTP.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/nstime.h"
#include "ns3/names.h"
#include "ns3/clock.h"
#include "ns3/clock-virtual.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-header2.h"
#include "ns3/gPTP-header.h"
#include "ns3/gPTP-packet.h"
#include <tgmath.h>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("GPTP");
NS_OBJECT_ENSURE_REGISTERED(GPTP);
TypeId
GPTP::GetTypeId()
{
static TypeId tid =
TypeId("ns3::GPTP")
.SetParent<Application>()
.SetGroupName("Applications")
.AddConstructor<GPTP>()
.AddAttribute("PdelayInterval",
"Period between two emission of pdelayRequest messages",
TimeValue(Seconds(1)),
MakeTimeAccessor(&GPTP::m_pdelayInterval),
MakeTimeChecker())
.AddAttribute("SyncInterval",
"Period between two emission of sync messages",
TimeValue(MilliSeconds(125)),
MakeTimeAccessor(&GPTP::m_syncInterval),
MakeTimeChecker())
.AddAttribute("Priority",
"GPTP frame priority",
UintegerValue(7),
MakeUintegerAccessor(&GPTP::m_priority),
MakeUintegerChecker<uint8_t>())
.AddTraceSource("Nr",
"Nr value",
MakeTraceSourceAccessor(&GPTP::m_NrTrace),
"ns3::GPTP::m_NrTrace")
.AddTraceSource("Pdelay",
"Pdelay value",
MakeTraceSourceAccessor(&GPTP::m_PdelayTrace),
"ns3::GPTP::m_PdelayTrace")
.AddTraceSource("Offset",
"Offset value",
MakeTraceSourceAccessor(&GPTP::m_OffsetTrace),
"ns3::TracedValueCallback::Time")
.AddTraceSource("ClockBeforeCorrection",
"Clock value before correction",
MakeTraceSourceAccessor(&GPTP::m_ClockBeforeCorrectionTrace),
"ns3::TracedValueCallback::Time")
.AddTraceSource("ClockAfterCorrection",
"Clock value after correction",
MakeTraceSourceAccessor(&GPTP::m_ClockAfterCorrectionTrace),
"ns3::TracedValueCallback::Time");
return tid;
}
GPTP::GPTP()
{
NS_LOG_FUNCTION(this);
}
GPTP::~GPTP()
{
NS_LOG_FUNCTION(this);
}
void
GPTP::SetMainClock(Ptr<Clock> c)
{
NS_LOG_FUNCTION(this);
m_mainClock = c;
}
void
GPTP::AddDomain(uint8_t domainId)
{
NS_ASSERT_MSG(m_mainClock != nullptr, "Main clock not set using 'void GPTP::SetMainClock(Ptr<Clock> c)' before calling 'void GPTP::AddDomain(uint8_t domainId)'");
Ptr<VirtualClock> c = CreateObject<VirtualClock>();
c->SetRefClock(m_mainClock);
AddDomain(domainId, c);
m_node->GetObject<TsnNode>()->AddClock(c);
}
void
GPTP::AddDomain(uint8_t domainId, Ptr<Clock> clock)
{
for(int i=0; i < (int)m_domains.size(); i++){
NS_ASSERT_MSG(m_domains[i].domainId != domainId,
"Domain #"<< (uint16_t)domainId <<" already exist on this node");
NS_ASSERT_MSG(m_domains[i].clock != clock,
"Clock "<< clock<<" already exist on this node");
}
m_domains.insert(m_domains.end(), {domainId, clock});
}
void
GPTP::AddPort(Ptr<TsnNetDevice> net, GPTPportState state, uint8_t domainId)
{
NS_LOG_FUNCTION(this);
bool domainExist = false;
for(int i=0; i < (int)m_domains.size(); i++){
if (m_domains[i].domainId == domainId)
{
domainExist = true;
}
}
NS_ASSERT_MSG(domainExist,
"Domain #"<< (uint16_t)domainId <<" was not created using AddDomain(uint8_t domainId, Ptr<Clock> clock)");
int id = -1;
for(int i=0; i < (int)m_ports.size(); i++){
if(m_ports[i].net == net)
{
id = i;
}
}
if (id == -1)
{
//Net device not in m_ports
GPTPPort entry = {net};
m_ports.insert(m_ports.end(), entry);
GPTPPortPerDomainStruc perDomain = {domainId, state};
m_ports.back().domains.insert(m_ports.back().domains.end(), perDomain);
}
else
{
//Net device in m_ports
GPTPPortPerDomainStruc perDomain = {domainId, state};
m_ports[id].domains.insert(m_ports[id].domains.end(), perDomain);
}
}
void
GPTP::DoDispose()
{
NS_LOG_FUNCTION(this);
Application::DoDispose();
}
void
GPTP::StartApplication()
{
NS_LOG_FUNCTION(this);
//Set m_clockIndentity according to IEEE1588-2019 - 7.5.2.2.2.2
uint8_t macBuffer[6];
m_ports[0].net->GetAddress().CopyTo(macBuffer);
m_baseClockIndentity = (((uint64_t)macBuffer[0] << 56 ) |
((uint64_t)macBuffer[1] << 48 ) |
((uint64_t)macBuffer[2] << 40 ) |
((uint64_t)macBuffer[3] << 32 ) |
((uint64_t)macBuffer[4] << 24 ) |
((uint64_t)macBuffer[5] << 16 ) |
0);;
//Setup RXCallback
for(int i=0; i < (int)m_ports.size(); i++){
m_ports[i].net->SetTransmitCallbackWithTimestamp(MakeCallback(&GPTP::TXCallback, this));
m_ports[i].net->SetReceiveCallbackWithTimestamp(MakeCallback(&GPTP::RXCallback, this));
}
//Send first pdelay_req
StartPdelayMechanism();
//Per domain, if GM, send first sync
for(int i=0; i <(int)m_domains.size(); i++)
{
// NS_LOG_INFO("Domain #" << (uint16_t)m_domains[i]);
if(IsGm(m_domains[i].domainId)){
StartSyncDistributionMechanism(m_domains[i].domainId);
}
}
}
void
GPTP::StopApplication()
{
NS_LOG_FUNCTION(this);
Simulator::Cancel(m_pdelayReqTxEvent);
Simulator::Cancel(m_syncTxEvent);
}
void
GPTP::StartPdelayMechanism()
{
NS_LOG_FUNCTION(this);
for(int i=0; i < (int)m_ports.size(); i++){
SendPdelayRequest(m_ports[i], (uint16_t)i);
}
m_pdelaySequenceId ++;
//Schedule next Pdelay mechanism
m_pdelayReqTxEvent = Simulator::Schedule(m_pdelayInterval, &GPTP::StartPdelayMechanism, this);
}
void
GPTP::StartSyncDistributionMechanism(uint8_t domainId)
{
NS_LOG_FUNCTION(this);
SendSync(domainId);
//Schedule next sync emission
m_syncTxEvent = Simulator::Schedule(m_syncInterval, &GPTP::StartSyncDistributionMechanism, this, domainId);
}
void
GPTP::SendSync(uint8_t domainId){
NS_LOG_FUNCTION(this);
for(int i=0; i < (int)m_ports.size(); i++){
for(int j=0; j < (int)m_ports[i].domains.size(); j++){
if(m_ports[i].domains[j].domainId == domainId){
if(m_ports[i].asCapableAcrossDomains && m_ports[i].domains[j].state == MASTER){
if(IsGm(domainId)){
m_ports[i].domains[j].syncPtr.sequenceId ++;
}
Ptr<PdelayPayload> payload = Create<PdelayPayload>();
Ptr<Packet> p = payload->GetPkt();
GptpHeader gptpHeader;
gptpHeader.SetMessageType(MESSAGETYPE_SYNC);
gptpHeader.SetMessageLenght(44);
gptpHeader.SetDomainNumber(domainId);
gptpHeader.SetTwoStepFlag(true);
gptpHeader.SetCorrectionField(0);
gptpHeader.SetClockIdentity(m_baseClockIndentity | domainId);
gptpHeader.SetPortIdentity(i);
gptpHeader.SetSequenceId(m_ports[i].domains[j].syncPtr.sequenceId);
uint8_t logSyncInteral = -std::log2(m_syncInterval.GetSeconds());
gptpHeader.SetLogMessageInterval(logSyncInteral);
p->AddHeader(gptpHeader);
m_ports[i].domains[j].syncPtr.pktUid = p->GetUid();
m_ports[i].domains[j].syncPtr.pktSent = true;
m_ports[i].net->SendWithSpecificFIFO(p, GPTP_MULTICASTMAC, GPTP_ETHERTYPE, m_priority);
}
}
}
}
}
void
GPTP::SendFollowUp(GPTPPort port, uint16_t portId, uint8_t id){
NS_LOG_FUNCTION(this);
//Compute rateRatio and correctionField
if(IsGm(m_ports[portId].domains[id].domainId))
{
m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset = 0;
m_ports[portId].domains[id].syncPtr.correctionField = 0;
}
else
{
Time syncEventIngressTimestamp = Time(Seconds(m_ports[portId].domains[id].syncPtr.syncReceiptTimestampSecond)) + Time(NanoSeconds(m_ports[portId].domains[id].syncPtr.syncReceiptTimestampNanoSecond));
Time syncEventEgressTimestamp = Time(Seconds(m_ports[portId].domains[id].syncPtr.syncOriginTimestampSecond)) + Time(NanoSeconds(m_ports[portId].domains[id].syncPtr.syncOriginTimestampNanoSecond));
Time upstreamTxTime = syncEventIngressTimestamp - Time(NanoSeconds(m_ports[portId].meanLinkDelay / m_ports[portId].neighborRateRatio)); //STD 802.1AS : 10.2.2.1.7
double r = m_ports[portId].neighborRateRatio*((m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset * pow(2,-41)) + 1);
m_ports[portId].domains[id].syncPtr.correctionField = m_ports[portId].domains[id].syncPtr.correctionField + r*((double)syncEventEgressTimestamp.GetNanoSeconds()-(double)upstreamTxTime.GetNanoSeconds())*pow(2,16);
m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset = (r - 1.0)*pow(2,41);
}
Ptr<FollowUpPayload> payload = Create<FollowUpPayload>(m_ports[portId].domains[id].syncPtr.preciseOriginTimestampSecond,
m_ports[portId].domains[id].syncPtr.preciseOriginTimestampNanoSecond,
m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset,
0,
0,0,0,
0);
Ptr<Packet> p = payload->GetPkt();
GptpHeader gptpHeader;
gptpHeader.SetMessageType(MESSAGETYPE_FOLLOWUP);
gptpHeader.SetMessageLenght(76);
gptpHeader.SetDomainNumber(m_ports[portId].domains[id].domainId);
gptpHeader.SetCorrectionField(m_ports[portId].domains[id].syncPtr.correctionField);
gptpHeader.SetClockIdentity(m_baseClockIndentity | m_ports[portId].domains[id].domainId);
gptpHeader.SetPortIdentity(portId);
gptpHeader.SetSequenceId(m_ports[portId].domains[id].syncPtr.sequenceId);
uint8_t logSyncInteral = -std::log2(m_syncInterval.GetSeconds());
gptpHeader.SetLogMessageInterval(logSyncInteral);
p->AddHeader(gptpHeader);
port.net->SendWithSpecificFIFO(p, GPTP_MULTICASTMAC, GPTP_ETHERTYPE, m_priority);
}
void
GPTP::SendPdelayRequest(GPTPPort port, uint16_t portId){
NS_LOG_FUNCTION(this);
m_ports[portId].rcvdPdelayResp = false;
m_ports[portId].rcvdPdelayRespFollowUp = false;
m_ports[portId].txPdelayReqPtr.correctionField = 0;
m_ports[portId].txPdelayReqPtr.sourceClockIndentity = m_baseClockIndentity;
m_ports[portId].txPdelayReqPtr.sourcePortIndentity = portId;
m_ports[portId].txPdelayReqPtr.sequenceId = m_pdelaySequenceId;
m_ports[portId].txPdelayReqPtr.requestOriginTimestampSecond = 0;
m_ports[portId].txPdelayReqPtr.requestOriginTimestampNanoSecond = 0;
Ptr<PdelayPayload> payload = Create<PdelayPayload>();
Ptr<Packet> p = payload->GetPkt();
GptpHeader gptpHeader;
gptpHeader.SetMessageType(MESSAGETYPE_PDELAYREQ);
gptpHeader.SetMessageLenght(54);
gptpHeader.SetCorrectionField(m_ports[portId].txPdelayReqPtr.correctionField);
gptpHeader.SetClockIdentity(m_ports[portId].txPdelayReqPtr.sourceClockIndentity);
gptpHeader.SetPortIdentity(m_ports[portId].txPdelayReqPtr.sourcePortIndentity);
gptpHeader.SetSequenceId(m_ports[portId].txPdelayReqPtr.sequenceId);
uint8_t logPdelayInteral = -std::log2(m_pdelayInterval.GetSeconds());
gptpHeader.SetLogMessageInterval(logPdelayInteral);
p->AddHeader(gptpHeader);
m_ports[portId].txPdelayReqPtr.pktUid = p->GetUid();
m_ports[portId].txPdelayReqPtr.pktSent = true;
port.net->SendWithSpecificFIFO(p, GPTP_MULTICASTMAC, GPTP_ETHERTYPE, m_priority);
}
void
GPTP::SendPdelayResp(Ptr<TsnNetDevice> dev, GptpHeader rxHeader, Time rxTimestamp)
{
uint16_t portId = GetPortIdFromNetDev(dev);
m_ports[portId].txPdelayRespPtr.correctionField = 0;
m_ports[portId].txPdelayRespPtr.sourceClockIndentity = m_baseClockIndentity;
m_ports[portId].txPdelayRespPtr.sourcePortIndentity = portId;
m_ports[portId].txPdelayRespPtr.sequenceId = rxHeader.GetSequenceId();
m_ports[portId].txPdelayRespPtr.requestReceiptTimestampSecond = rxTimestamp.GetSeconds();
m_ports[portId].txPdelayRespPtr.requestReceiptTimestampNanoSecond = rxTimestamp.GetNanoSeconds() % NANOINSECOND;
m_ports[portId].txPdelayRespPtr.requestingClockIdentity = rxHeader.GetClockIdentity();
m_ports[portId].txPdelayRespPtr.requestingPortIdentity = rxHeader.GetPortIdentity();
m_ports[portId].txPdelayRespPtr.responseReceiptTimestampSecond = 0;
m_ports[portId].txPdelayRespPtr.responseReceiptTimestampNanoSecond = 0;
Ptr<PdelayPayload> payload = Create<PdelayPayload>(m_ports[portId].txPdelayRespPtr.requestReceiptTimestampSecond,
m_ports[portId].txPdelayRespPtr.requestReceiptTimestampNanoSecond,
m_ports[portId].txPdelayRespPtr.requestingClockIdentity,
m_ports[portId].txPdelayRespPtr.requestingPortIdentity);
Ptr<Packet> p = payload->GetPkt();
GptpHeader gptpHeader;
gptpHeader.SetMessageType(MESSAGETYPE_PDELAYRESP);
gptpHeader.SetMessageLenght(54);
gptpHeader.SetTwoStepFlag(true);
gptpHeader.SetClockIdentity(m_ports[portId].txPdelayRespPtr.sourceClockIndentity);
gptpHeader.SetPortIdentity(m_ports[portId].txPdelayRespPtr.sourcePortIndentity);
gptpHeader.SetSequenceId(m_ports[portId].txPdelayRespPtr.sequenceId);
gptpHeader.SetLogMessageInterval(0x7F);
p->AddHeader(gptpHeader);
m_ports[portId].txPdelayRespPtr.pktUid = p->GetUid();
m_ports[portId].txPdelayRespPtr.pktSent = true;
dev->SendWithSpecificFIFO(p, GPTP_MULTICASTMAC, GPTP_ETHERTYPE, m_priority);
}
void
GPTP::SendPdelayRespFup(Ptr<TsnNetDevice> dev, Time txTimestamp)
{
uint16_t portId = GetPortIdFromNetDev(dev);
Ptr<PdelayPayload> payload = Create<PdelayPayload>(txTimestamp.GetSeconds(),
txTimestamp.GetNanoSeconds()%NANOINSECOND,
m_ports[portId].txPdelayRespPtr.requestingClockIdentity,
m_ports[portId].txPdelayRespPtr.requestingPortIdentity);
Ptr<Packet> p = payload->GetPkt();
GptpHeader gptpHeader;
gptpHeader.SetMessageType(MESSAGETYPE_PDELAYRESPFOLLOWUP);
gptpHeader.SetMessageLenght(54);
gptpHeader.SetClockIdentity(m_ports[portId].txPdelayRespPtr.sourceClockIndentity);
gptpHeader.SetPortIdentity(m_ports[portId].txPdelayRespPtr.sourcePortIndentity);
gptpHeader.SetSequenceId(m_ports[portId].txPdelayRespPtr.sequenceId);
gptpHeader.SetLogMessageInterval(0x7F);
p->AddHeader(gptpHeader);
dev->SendWithSpecificFIFO(p, GPTP_MULTICASTMAC, GPTP_ETHERTYPE, m_priority);
}
void
GPTP::handleSync(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp)
{
NS_LOG_FUNCTION(this);
uint16_t portId = GetPortIdFromNetDev(dev);
int id = GetDomainKeyFromNetDev(dev, portId, header.GetDomainNumber());
if(id != -1 && m_ports[portId].domains[id].state == SLAVE)
{
NS_LOG_INFO("Handle sync for domains #" << (uint16_t)m_ports[portId].domains[id].domainId);
m_ports[portId].domains[id].syncPtr.correctionField = 0;
m_ports[portId].domains[id].syncPtr.sourceClockIndentity = header.GetClockIdentity();
m_ports[portId].domains[id].syncPtr.sourcePortIndentity = header.GetPortIdentity();
m_ports[portId].domains[id].syncPtr.sequenceId = header.GetSequenceId();
m_ports[portId].domains[id].syncPtr.syncOriginTimestampSecond = 0;
m_ports[portId].domains[id].syncPtr.syncOriginTimestampNanoSecond = 0;
m_ports[portId].domains[id].syncPtr.syncReceiptTimestampSecond = rxTimestamp.GetSeconds();
m_ports[portId].domains[id].syncPtr.syncReceiptTimestampNanoSecond = rxTimestamp.GetNanoSeconds() % NANOINSECOND;
}
}
void
GPTP::handleFollowUp(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp)
{
NS_LOG_FUNCTION(this);
uint16_t portId = GetPortIdFromNetDev(dev);
int id = GetDomainKeyFromNetDev(dev, portId, header.GetDomainNumber());
if(id != -1 && m_ports[portId].domains[id].state == SLAVE)
{
if(m_ports[portId].domains[id].syncPtr.sequenceId == header.GetSequenceId() &&
m_ports[portId].domains[id].syncPtr.sourceClockIndentity == header.GetClockIdentity() &&
m_ports[portId].domains[id].syncPtr.sourcePortIndentity == header.GetPortIdentity())
{
NS_LOG_INFO("Handle fup for domains #" << (uint16_t)m_ports[portId].domains[id].domainId);
uint8_t payload[42] = {};
pkt->CopyData(payload, 42);
uint64_t timestampSecond = 0;
uint32_t timestampNanoSecond = 0;
uint32_t cumulativeScaledRateOffset = 0;
readFollowUpPayload(payload, timestampSecond, timestampNanoSecond, cumulativeScaledRateOffset);
double r = (cumulativeScaledRateOffset * pow(2,-41)) + 1;
m_ports[portId].domains[id].syncPtr.preciseOriginTimestampSecond = timestampSecond;
m_ports[portId].domains[id].syncPtr.preciseOriginTimestampNanoSecond = timestampNanoSecond;
m_ports[portId].domains[id].syncPtr.correctionField = header.GetCorrectionField();
m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset = cumulativeScaledRateOffset;
//Correct local clock
Time offset = computeOffset(portId, id, r*m_ports[portId].neighborRateRatio);
Ptr<Clock> domainClock = getClockFromDomainId(m_ports[portId].domains[id].domainId);
domainClock->SetOffsetTime(offset);
m_OffsetTrace(offset);
m_ClockBeforeCorrectionTrace(domainClock->GetLocalTime() - offset);
m_ClockAfterCorrectionTrace(domainClock->GetLocalTime());
// NS_LOG_INFO("At " << Simulator::Now().GetNanoSeconds() << " " << Names::FindName(m_ports[portId].net) << " : " << m_clock->GetLocalTime().GetNanoSeconds() << " (" << m_clock->GetLocalTime().GetNanoSeconds()-Simulator::Now().GetNanoSeconds() <<")");
//Forward sync to master port on this node
for(int i=0; i < (int)m_ports.size(); i++){
for (int j=0; j < (int)m_ports[i].domains.size(); j++)
{
if(m_ports[i].domains[j].domainId==m_ports[portId].domains[id].domainId && m_ports[i].domains[j].state==MASTER)
{
m_ports[i].domains[j].syncPtr.sequenceId = m_ports[portId].domains[id].syncPtr.sequenceId;
m_ports[i].domains[j].syncPtr.correctionField = m_ports[portId].domains[id].syncPtr.correctionField;
m_ports[i].domains[j].syncPtr.preciseOriginTimestampSecond = m_ports[portId].domains[id].syncPtr.preciseOriginTimestampSecond;
m_ports[i].domains[j].syncPtr.preciseOriginTimestampNanoSecond = m_ports[portId].domains[id].syncPtr.preciseOriginTimestampNanoSecond;
m_ports[i].domains[j].syncPtr.syncReceiptTimestampSecond = m_ports[portId].domains[id].syncPtr.syncReceiptTimestampSecond;
m_ports[i].domains[j].syncPtr.syncReceiptTimestampNanoSecond = m_ports[portId].domains[id].syncPtr.syncReceiptTimestampNanoSecond;
m_ports[i].domains[j].syncPtr.cumulativeScaledRateOffset = m_ports[portId].domains[id].syncPtr.cumulativeScaledRateOffset;
}
}
}
SendSync(m_ports[portId].domains[id].domainId);
}
}
}
void
GPTP::handlePdelayResp(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp)
{
uint16_t portId = GetPortIdFromNetDev(dev);
uint8_t payload[20] = {};
pkt->CopyData(payload, 20);
uint64_t timestampSecond = 0;
uint32_t timestampNanoSecond = 0;
uint64_t clockIdentity = 0;
uint16_t portIdentity = 0;
readPledayPayload(payload, timestampSecond, timestampNanoSecond, clockIdentity, portIdentity);
if(clockIdentity != m_baseClockIndentity || portIdentity!=portId)
{
return;
}
if(header.GetSequenceId() == m_pdelaySequenceId-1)
{
m_ports[portId].rcvdPdelayResp = true;
m_ports[portId].rcvdPdelayRespPtr.correctionField = header.GetCorrectionField();
m_ports[portId].rcvdPdelayRespPtr.sourceClockIndentity = header.GetClockIdentity();
m_ports[portId].rcvdPdelayRespPtr.sourcePortIndentity = header.GetPortIdentity();
m_ports[portId].rcvdPdelayRespPtr.sequenceId = header.GetSequenceId();
m_ports[portId].rcvdPdelayRespPtr.requestReceiptTimestampSecond = timestampSecond;
m_ports[portId].rcvdPdelayRespPtr.requestReceiptTimestampNanoSecond = timestampNanoSecond;
m_ports[portId].rcvdPdelayRespPtr.requestingClockIdentity = m_baseClockIndentity;
m_ports[portId].rcvdPdelayRespPtr.requestingPortIdentity = portId;
m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampSecond = rxTimestamp.GetSeconds();
m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampNanoSecond = rxTimestamp.GetNanoSeconds() % NANOINSECOND;
}
else{
ResetPdelay(portId);
}
}
void
GPTP::handlePdelayRespFup(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt)
{
uint16_t portId = GetPortIdFromNetDev(dev);
uint8_t payload[20] = {};
pkt->CopyData(payload, 20);
uint64_t timestampSecond = 0;
uint32_t timestampNanoSecond = 0;
uint64_t clockIdentity = 0;
uint16_t portIdentity = 0;
readPledayPayload(payload, timestampSecond, timestampNanoSecond, clockIdentity, portIdentity);
if(clockIdentity != m_baseClockIndentity || portIdentity!=portId)
{
return;
}
else if(m_ports[portId].rcvdPdelayResp && header.GetSequenceId() == m_pdelaySequenceId-1)
{
m_ports[portId].rcvdPdelayRespFollowUp = true;
m_ports[portId].rcvdPdelayRespFollowUpPtr.correctionField = header.GetCorrectionField();
m_ports[portId].rcvdPdelayRespFollowUpPtr.sourceClockIndentity = header.GetClockIdentity();
m_ports[portId].rcvdPdelayRespFollowUpPtr.sourcePortIndentity = header.GetPortIdentity();
m_ports[portId].rcvdPdelayRespFollowUpPtr.sequenceId = header.GetSequenceId();
m_ports[portId].rcvdPdelayRespFollowUpPtr.requestingClockIdentity = m_baseClockIndentity;
m_ports[portId].rcvdPdelayRespFollowUpPtr.requestingPortIdentity = portId;
m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampSecond = timestampSecond;
m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampNanoSecond = timestampNanoSecond;
}
else{
ResetPdelay(portId);
return;
}
m_ports[portId].neighborRateRatio = computePdelayRateRatio(portId);
m_ports[portId].meanLinkDelay = computePropTime(portId);
m_NrTrace(m_ports[portId].net, m_ports[portId].neighborRateRatio );
m_PdelayTrace(m_ports[portId].net, m_ports[portId].meanLinkDelay );
if(m_ports[portId].neighborRateRatioValid && m_ports[portId].meanLinkDelay <= m_meanLinkDelayThresh && m_ports[portId].meanLinkDelay >= -m_meanLinkDelayThresh)
{
m_ports[portId].asCapableAcrossDomains = true;
// NS_LOG_INFO("m_ports[portId].neighborRateRatio = " << m_ports[portId].neighborRateRatio);
// NS_LOG_INFO("m_ports[portId].meanLinkDelay = " << m_ports[portId].meanLinkDelay);
}
else
{
m_ports[portId].asCapableAcrossDomains = false;
}
}
double
GPTP::computePdelayRateRatio(uint16_t portId)
{
NS_LOG_FUNCTION(this);
Time t3 = Time(Seconds(m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampSecond)) + Time(NanoSeconds(m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampNanoSecond));
Time t4 = Time(Seconds(m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampSecond)) + Time(NanoSeconds(m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampNanoSecond));
Time t3_prime = m_ports[portId].t3_prime;
Time t4_prime = m_ports[portId].t4_prime;
double nr = (double)(t3.GetNanoSeconds() - t3_prime.GetNanoSeconds()) / (double)(t4.GetNanoSeconds() - t4_prime.GetNanoSeconds());
if(t3_prime != Time(0) && t4_prime != Time(0)){
m_ports[portId].neighborRateRatioValid = true;
}
m_ports[portId].t3_prime = t3;
m_ports[portId].t4_prime = t4;
return nr;
}
double
GPTP::computePropTime(uint16_t portId)
{
NS_LOG_FUNCTION(this);
double nr = m_ports[portId].neighborRateRatio;
Time t1 = Time(Seconds(m_ports[portId].txPdelayReqPtr.requestOriginTimestampSecond)) + Time(NanoSeconds(m_ports[portId].txPdelayReqPtr.requestOriginTimestampNanoSecond));
Time t2 = Time(Seconds(m_ports[portId].rcvdPdelayRespPtr.requestReceiptTimestampSecond)) + Time(NanoSeconds(m_ports[portId].rcvdPdelayRespPtr.requestReceiptTimestampNanoSecond));
Time t3 = Time(Seconds(m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampSecond)) + Time(NanoSeconds(m_ports[portId].rcvdPdelayRespFollowUpPtr.responseOriginTimestampNanoSecond));
Time t4 = Time(Seconds(m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampSecond)) + Time(NanoSeconds(m_ports[portId].rcvdPdelayRespPtr.responseReceiptTimestampNanoSecond));
double d = (nr*(t4.GetNanoSeconds()-t1.GetNanoSeconds()) - (t3.GetNanoSeconds()-t2.GetNanoSeconds()))/2.0;
return d;
}
void
GPTP::ResetPdelay(uint16_t portId)
{
NS_LOG_FUNCTION(this);
m_ports[portId].rcvdPdelayResp = false;
m_ports[portId].rcvdPdelayRespFollowUp = false;
m_ports[portId].lostResponses ++;
}
Time
GPTP::computeOffset(uint16_t portId, uint16_t id, double r)
{
NS_LOG_FUNCTION(this);
Time syncEventIngressTimestamp = Time(Seconds( m_ports[portId].domains[id].syncPtr.syncReceiptTimestampSecond)) + Time(NanoSeconds( m_ports[portId].domains[id].syncPtr.syncReceiptTimestampNanoSecond));
Time upstreamTxTime = syncEventIngressTimestamp - Time(NanoSeconds(m_ports[portId].meanLinkDelay / m_ports[portId].neighborRateRatio)); //STD 802.1AS : 10.2.2.1.7
Time localTime = getClockFromDomainId(m_ports[portId].domains[id].domainId)->GetUncorrectedLocalTime();
Time preciseOriginTime = Time(Seconds( m_ports[portId].domains[id].syncPtr.preciseOriginTimestampSecond)) + Time(NanoSeconds( m_ports[portId].domains[id].syncPtr.preciseOriginTimestampNanoSecond));
Time gmTime = preciseOriginTime + Time(NanoSeconds( m_ports[portId].domains[id].syncPtr.correctionField* pow(2,-16))) + r*(localTime - upstreamTxTime);
Time offset = gmTime - localTime;
NS_LOG_INFO("Offset " << offset);
return offset;
}
uint16_t
GPTP::GetPortIdFromNetDev(Ptr<TsnNetDevice> dev)
{
NS_LOG_FUNCTION(this);
uint16_t portId = 65535;
for(int i=0; i < (int)m_ports.size(); i++){
if(dev == m_ports[i].net)
{
portId = i;
}
}
return portId;
}
int
GPTP::GetDomainKeyFromNetDev(Ptr<TsnNetDevice> dev, uint16_t portId, uint8_t domainId)
{
NS_LOG_FUNCTION(this);
for(int k=0; k <(int)m_ports[portId].domains.size(); k++){
if(m_ports[portId].domains[k].domainId == domainId){
return k;
}
}
return -1;
}
Ptr<Clock>
GPTP::getClockFromDomainId(uint8_t domainId)
{
for(int i=0; i <(int)m_domains.size(); i++){
if(m_domains[i].domainId == domainId)
{
return m_domains[i].clock;
}
}
return nullptr;
}
bool
GPTP::IsGm(uint8_t domainId)
{
for(int j=0; j <(int)m_ports.size(); j++){
for(int k=0; k <(int)m_ports[j].domains.size(); k++){
if(m_ports[j].domains[k].domainId == domainId && m_ports[j].domains[k].state != MASTER){
return false;
}
}
}
return true;
}
void
GPTP::readPledayPayload(uint8_t* payload, uint64_t& timestampSecond, uint32_t& timestampNanoSecond, uint64_t& clockIdentity, uint16_t& portIdentity)
{
NS_LOG_FUNCTION(this);
timestampSecond = ((uint64_t)payload[0] <<40) | ((uint64_t)payload[1] <<32) | ((uint64_t)payload[2] <<24) | ((uint64_t)payload[3] <<16) | ((uint64_t)payload[4] <<8) | payload[5];
timestampNanoSecond = ((uint32_t)payload[6] <<24) | ((uint32_t)payload[7] <<16) | ((uint32_t)payload[8] <<8) | payload[9];
clockIdentity = ((uint64_t)payload[10] <<56) | ((uint64_t)payload[11] <<48) | ((uint64_t)payload[12] <<40) | ((uint64_t)payload[13] <<32) | ((uint64_t)payload[14] <<24) | ((uint64_t)payload[15] <<16) | ((uint64_t)payload[16] <<8) | payload[17];
portIdentity = (payload[18] <<8) | payload[19];
}
void
GPTP::readFollowUpPayload(uint8_t* payload, uint64_t& timestampSecond, uint32_t& timestampNanoSecond, uint32_t& cumulativeScaledRateOffset)
{
NS_LOG_FUNCTION(this);
timestampSecond = ((uint64_t)payload[0] <<40) | ((uint64_t)payload[1] <<32) | ((uint64_t)payload[2] <<24) | ((uint64_t)payload[3] <<16) | ((uint64_t)payload[4] <<8) | payload[5];
timestampNanoSecond = ((uint32_t)payload[6] <<24) | ((uint32_t)payload[7] <<16) | ((uint32_t)payload[8] <<8) | payload[9];
cumulativeScaledRateOffset = ((uint32_t)payload[20] <<24) | ((uint32_t)payload[21] <<16) | ((uint32_t)payload[22] <<8) | payload[23];
}
bool
GPTP::RXCallback(Ptr<TsnNetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender, Time rxTimestamp)
{
NS_LOG_FUNCTION(this);
if(mode==GPTP_ETHERTYPE)
{
Ptr<Packet> pktCopy = pkt->Copy();
GptpHeader header;
pktCopy->RemoveHeader(header);
if(header.GetMessageType() == MESSAGETYPE_SYNC)
{
Simulator::Schedule(m_pktProcessingTime, &GPTP::handleSync, this, dev, header, pktCopy, rxTimestamp);
}
else if(header.GetMessageType() == MESSAGETYPE_FOLLOWUP)
{
Simulator::Schedule(m_pktProcessingTime, &GPTP::handleFollowUp, this, dev, header, pktCopy, rxTimestamp);
}
else if(header.GetMessageType() == MESSAGETYPE_PDELAYREQ)
{
Simulator::Schedule(m_pktProcessingTime, &GPTP::SendPdelayResp, this, dev, header, rxTimestamp);
}
else if(header.GetMessageType() == MESSAGETYPE_PDELAYRESP)
{
Simulator::Schedule(m_pktProcessingTime, &GPTP::handlePdelayResp, this, dev, header, pktCopy, rxTimestamp);
}
else if(header.GetMessageType() == MESSAGETYPE_PDELAYRESPFOLLOWUP)
{
Simulator::Schedule(m_pktProcessingTime, &GPTP::handlePdelayRespFup, this, dev, header, pktCopy);
}
}
return true;
}
bool
GPTP::TXCallback(Ptr<const Packet> pkt, Time txTimestamp)
{
NS_LOG_FUNCTION(this);
Ptr<Packet> pktCopy = pkt->Copy();
EthernetHeader2 ethHeader;
pktCopy->RemoveHeader(ethHeader);
GptpHeader gptpHeader;
pktCopy->RemoveHeader(gptpHeader);
if(ethHeader.GetEthertype() == GPTP_ETHERTYPE)
{
for(int i=0; i < (int)m_ports.size(); i++){
for(int k = 0; k < (int)m_ports[i].domains.size(); k++){
if(m_ports[i].domains[k].syncPtr.pktSent && pkt->GetUid() == m_ports[i].domains[k].syncPtr.pktUid)
{
m_ports[i].domains[k].syncPtr.syncOriginTimestampSecond = txTimestamp.GetSeconds();
m_ports[i].domains[k].syncPtr.syncOriginTimestampNanoSecond = txTimestamp.GetNanoSeconds() % NANOINSECOND;
if(IsGm(gptpHeader.GetDomainNumber()))
{
Ptr<Clock> domainClock = getClockFromDomainId(gptpHeader.GetDomainNumber());
Time diff = m_mainClock->GetLocalTime() - domainClock->GetLocalTime();
txTimestamp = txTimestamp - diff;
m_ports[i].domains[k].syncPtr.preciseOriginTimestampSecond = txTimestamp.GetSeconds();
m_ports[i].domains[k].syncPtr.preciseOriginTimestampNanoSecond = txTimestamp.GetNanoSeconds() % NANOINSECOND;
}
Simulator::Schedule(m_TimestampProcessingTime, &GPTP::SendFollowUp, this, m_ports[i], i, k);
return true;
}
else if(m_ports[i].txPdelayReqPtr.pktSent && pkt->GetUid() == m_ports[i].txPdelayReqPtr.pktUid)
{
m_ports[i].txPdelayReqPtr.requestOriginTimestampSecond = txTimestamp.GetSeconds();
m_ports[i].txPdelayReqPtr.requestOriginTimestampNanoSecond = txTimestamp.GetNanoSeconds() % NANOINSECOND;
return true;
}
else if(m_ports[i].txPdelayRespPtr.pktSent && pkt->GetUid() == m_ports[i].txPdelayRespPtr.pktUid)
{
Simulator::Schedule(m_TimestampProcessingTime, &GPTP::SendPdelayRespFup, this, m_ports[i].net, txTimestamp);
return true;
}
}
}
}
return true;
}
} // Namespace ns3

219
contrib/tsn/model/gPTP.h Normal file
View File

@@ -0,0 +1,219 @@
#ifndef GPTP_H
#define GPTP_H
#include "ns3/application.h"
#include "ns3/clock.h"
#include "ns3/tsn-net-device.h"
#include "ns3/gPTP-header.h"
#define NANOINSECOND 1000000000
namespace ns3
{
class GPTP : public Application
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId();
GPTP();
~GPTP() override;
/**
* Enumeration of the states of the PTP port.
*/
enum GPTPportState
{
MASTER,
SLAVE,
PASSIVE,
};
void SetMainClock(Ptr<Clock> c);
void AddDomain(uint8_t domainId);
void AddDomain(uint8_t domainId, Ptr<Clock> clock);
void AddPort(Ptr<TsnNetDevice> net, GPTPportState state, uint8_t domainId);
protected:
void DoDispose() override;
TracedCallback<Ptr<TsnNetDevice>,
double> m_NrTrace;
TracedCallback<Ptr<TsnNetDevice>,
double> m_PdelayTrace;
TracedCallback<Time> m_OffsetTrace;
TracedCallback<Time> m_ClockBeforeCorrectionTrace;
TracedCallback<Time> m_ClockAfterCorrectionTrace;
private:
struct syncPtrStruc
{
uint64_t correctionField = 0;
uint64_t sourceClockIndentity = 0;
uint16_t sourcePortIndentity = 0;
uint16_t sequenceId = 0;
uint64_t preciseOriginTimestampSecond = 0;
uint32_t preciseOriginTimestampNanoSecond = 0;
uint64_t syncOriginTimestampSecond = 0;
uint32_t syncOriginTimestampNanoSecond = 0;
uint64_t pktUid = 0;
bool pktSent = false;
uint64_t syncReceiptTimestampSecond = 0;
uint32_t syncReceiptTimestampNanoSecond = 0;
uint32_t cumulativeScaledRateOffset = 0;
};
struct pdelayReqPtrStruc
{
uint64_t correctionField = 0;
uint64_t sourceClockIndentity = 0;
uint16_t sourcePortIndentity = 0;
uint16_t sequenceId = 0;
uint64_t requestOriginTimestampSecond = 0;
uint32_t requestOriginTimestampNanoSecond = 0;
uint64_t pktUid = 0;
bool pktSent = false;
};
struct pdelayRespStruc
{
uint64_t correctionField = 0;
uint64_t sourceClockIndentity = 0;
uint16_t sourcePortIndentity = 0;
uint16_t sequenceId = 0;
uint64_t requestReceiptTimestampSecond = 0;
uint32_t requestReceiptTimestampNanoSecond = 0;
uint64_t requestingClockIdentity = 0;
uint16_t requestingPortIdentity = 0;
uint64_t responseReceiptTimestampSecond = 0;
uint32_t responseReceiptTimestampNanoSecond = 0;
uint64_t pktUid = 0;
bool pktSent = false;
};
struct pdelayRespFollowUpStruc
{
uint64_t correctionField = 0;
uint64_t sourceClockIndentity = 0;
uint16_t sourcePortIndentity = 0;
uint16_t sequenceId = 0;
uint64_t responseOriginTimestampSecond = 0;
uint32_t responseOriginTimestampNanoSecond = 0;
uint64_t requestingClockIdentity = 0;
uint16_t requestingPortIdentity = 0;
};
struct GPTPPortPerDomainStruc
{
uint8_t domainId;
GPTPportState state;
syncPtrStruc syncPtr;
};
/**
* \ingroup tsn
* Structure holding port, there states and the PTPinstance datas.
*/
struct GPTPPort
{
Ptr<TsnNetDevice> net;
std::vector<GPTPPortPerDomainStruc> domains = {};
bool rcvdPdelayResp = false; //STD 802.1AS-2020 : 11.2.19.2.2
pdelayRespStruc rcvdPdelayRespPtr; //STD 802.1AS-2020 : 11.2.19.2.3
bool rcvdPdelayRespFollowUp = false; //STD 802.1AS-2020 : 11.2.19.2.4
pdelayRespFollowUpStruc rcvdPdelayRespFollowUpPtr; //STD 802.1AS-2020 : 11.2.19.2.5
pdelayReqPtrStruc txPdelayReqPtr; //STD 802.1AS-2020 : 11.2.19.2.6
uint16_t lostResponses; //STD 802.1AS-2020 : 11.2.19.2.9
bool neighborRateRatioValid = false; //STD 802.1AS-2020 : 11.2.19.2.10
pdelayRespStruc txPdelayRespPtr;
Time t3_prime = Time(0);
Time t4_prime = Time(0);
double neighborRateRatio = 1;
double meanLinkDelay = 0;
bool asCapableAcrossDomains = false;
};
struct domainStruc
{
uint8_t domainId;
Ptr<Clock> clock;
};
void StartApplication() override;
void StopApplication() override;
void StartPdelayMechanism();
void StartSyncDistributionMechanism(uint8_t domainId);
bool RXCallback(Ptr<TsnNetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender, Time rxTimestamp);
bool TXCallback(Ptr<const Packet> pkt, Time txTimestamp);
void SendSync(uint8_t domainId);
void SendFollowUp(GPTPPort port, uint16_t portId, uint8_t id);
void SendPdelayRequest(GPTPPort port, uint16_t portId);
void SendPdelayResp(Ptr<TsnNetDevice> dev, GptpHeader rxHeader, Time rxTimestamp);
void SendPdelayRespFup(Ptr<TsnNetDevice> dev, Time txTimestamp);
void handleSync(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp);
void handleFollowUp(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp);
void handlePdelayResp(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt, Time rxTimestamp);
void handlePdelayRespFup(Ptr<TsnNetDevice> dev, GptpHeader header, Ptr<Packet> pkt);
void ResetPdelay(uint16_t portId);
double computePdelayRateRatio(uint16_t portId); //STD 802.1AS-2020 : 11.2.19.3.2
double computePropTime(uint16_t portId); //STD 802.1AS-2020 : 11.2.19.3.3
Time computeOffset(uint16_t portId, uint16_t id, double r);
uint16_t GetPortIdFromNetDev(Ptr<TsnNetDevice> dev);
int GetDomainKeyFromNetDev(Ptr<TsnNetDevice> dev, uint16_t portId, uint8_t domainId);
Ptr<Clock> getClockFromDomainId(uint8_t domainId);
bool IsGm(uint8_t domainId);
void readPledayPayload(uint8_t* payload, uint64_t& timestampSecond, uint32_t& timestampNanoSecond, uint64_t& clockIdentity, uint16_t& portIdentity);
void readFollowUpPayload(uint8_t* payload, uint64_t& timestampSecond, uint32_t& timestampNanoSecond, uint32_t& cumulativeScaledRateOffset);
uint16_t m_pdelaySequenceId = 0;
double m_meanLinkDelayThresh = 800;
Ptr<Clock> m_mainClock = nullptr;
uint64_t m_baseClockIndentity;
Time m_pdelayInterval;
Time m_syncInterval;
uint8_t m_priority;
std::vector<domainStruc> m_domains = {};
const Mac48Address GPTP_MULTICASTMAC = Mac48Address("01:80:C2:00:00:0E");
static const uint16_t GPTP_ETHERTYPE = 0x88F7;
static const uint8_t MESSAGETYPE_SYNC = 0; //STD 802.1AS-2020 : Table 11-5
static const uint8_t MESSAGETYPE_PDELAYREQ = 2; //STD 802.1AS-2020 : Table 11-5
static const uint8_t MESSAGETYPE_PDELAYRESP = 3; //STD 802.1AS-2020 : Table 11-5
static const uint8_t MESSAGETYPE_FOLLOWUP = 8; //STD 802.1AS-2020 : Table 11-5
static const uint8_t MESSAGETYPE_PDELAYRESPFOLLOWUP = 10; //STD 802.1AS-2020 : Table 11-5
EventId m_pdelayReqTxEvent;
EventId m_syncTxEvent;
std::vector<GPTPPort> m_ports;
Time m_pktProcessingTime = Time(MicroSeconds(20));
Time m_TimestampProcessingTime = Time(MicroSeconds(20));
};
} // namespace ns3
#endif /* GPTP_H */

View File

@@ -0,0 +1,156 @@
#include "psfp-flow-meter-instance.h"
#include "ns3/log.h"
#include "ns3/integer.h"
#include "ns3/uinteger.h"
#include "ns3/boolean.h"
#include "ns3/simulator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-trailer.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("FlowMeterInstance");
NS_OBJECT_ENSURE_REGISTERED(FlowMeterInstance);
TypeId
FlowMeterInstance::GetTypeId()
{
static TypeId tid =
TypeId("ns3::FlowMeterInstance")
.SetParent<Object>()
.SetGroupName("Tsn")
.AddConstructor<FlowMeterInstance>()
.AddAttribute("CIR",
"CIR parameter in bit/s",
DataRateValue(DataRate("0Gb/s")),
MakeDataRateAccessor(&FlowMeterInstance::m_cir),
MakeDataRateChecker())
.AddAttribute("CBS",
"CBS parameter in bit",
UintegerValue(0),
MakeUintegerAccessor(&FlowMeterInstance::m_cbs),
MakeUintegerChecker<uint32_t>())
.AddAttribute("EIR",
"EIR parameter in bit/s",
DataRateValue(DataRate("0Gb/s")),
MakeDataRateAccessor(&FlowMeterInstance::m_eir),
MakeDataRateChecker())
.AddAttribute("EBS",
"EBS parameter in bit",
UintegerValue(0),
MakeUintegerAccessor(&FlowMeterInstance::m_ebs),
MakeUintegerChecker<uint32_t>())
.AddAttribute("DropOnYellow",
"Drop on yellow function activation boolean",
BooleanValue(false),
MakeBooleanAccessor(&FlowMeterInstance::m_dropOnYellow),
MakeBooleanChecker())
.AddAttribute("CF",
"coupling flag boolean",
BooleanValue(false),
MakeBooleanAccessor(&FlowMeterInstance::m_cf),
MakeBooleanChecker())
.AddAttribute("MarkAllFramesRedEnable",
"MarkAllFramesRedEnable function activation boolean",
BooleanValue(false),
MakeBooleanAccessor(&FlowMeterInstance::m_markAllFramesRedEnable),
MakeBooleanChecker());
return tid;
}
FlowMeterInstance::FlowMeterInstance()
{
NS_LOG_FUNCTION(this);
m_lastTokenUpdateTime = Simulator::Now();
m_markAllFramesRed = false;
}
FlowMeterInstance::~FlowMeterInstance()
{
NS_LOG_FUNCTION(this);
}
bool
FlowMeterInstance::Test(Ptr<Packet> packet)
{
NS_LOG_FUNCTION(this);
updateTokenBuckets();
Ptr<Packet> payload = packet->Copy();
EthernetHeader2 ethHeader;
EthernetTrailer trailer;
payload->RemoveHeader(ethHeader);
payload->RemoveTrailer(trailer);
if (m_markAllFramesRed){
//Mark all frame = drop all
NS_LOG_INFO("[PSFP] Packet #" << payload->GetUid() << " dropped due to markAllFrameRed");
return true;
}
NS_LOG_INFO("pkt size = " << payload->GetSize());
//First token bucket test
if (payload->GetSize() <= m_committedToken) {
//Mark green
m_committedToken -= payload->GetSize();
NS_LOG_INFO("[PSFP] Packet #" << payload->GetUid() << " marked green");
return false;
}
//Second token bucket test
if (payload->GetSize() <= m_excessToken) {
//Mark yellow
m_excessToken -= payload->GetSize();
if(m_dropOnYellow){
NS_LOG_INFO("[PSFP] Packet #" << payload->GetUid() << " marked yellow and dropped");
return true;
}
else{
NS_LOG_INFO("[PSFP] Packet #" << payload->GetUid() << " marked yellow and passed");
return false;
}
}
else{
//Mark red
NS_LOG_INFO("[PSFP] Packet #" << payload->GetUid() << " marked red (committedToken=" << m_committedToken << ", need " << payload->GetSize() << " tokens to pass)");
if(m_markAllFramesRedEnable){
m_markAllFramesRed = true;
}
return true;
}
}
void
FlowMeterInstance::updateTokenBuckets()
{
NS_LOG_FUNCTION(this);
uint16_t tokenOverflow = 0;
Time sinceLastUpdate = Simulator::Now() - m_lastTokenUpdateTime;
m_committedToken = m_committedToken + (sinceLastUpdate.GetSeconds() * m_cir.GetBitRate())/8.;
if (m_committedToken > m_cbs){
if (m_cf){
tokenOverflow = m_committedToken - m_cbs;
}
m_committedToken = m_cbs;
}
m_excessToken = m_excessToken + (sinceLastUpdate.GetSeconds() * m_eir.GetBitRate())/8. + tokenOverflow;
if (m_excessToken>m_ebs){
m_excessToken = m_ebs;
}
m_lastTokenUpdateTime = Simulator::Now();
NS_LOG_INFO("m_committedToken = " << m_committedToken);
NS_LOG_INFO("m_excessToken = " << m_excessToken);
}
};

View File

@@ -0,0 +1,61 @@
#ifndef FLOW_METER_INSTANCE_H
#define FLOW_METER_INSTANCE_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/packet.h"
#include "ns3/data-rate.h"
namespace ns3
{
class FlowMeterInstance: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a FlowMeterInstance
*/
FlowMeterInstance();
/**
* Destroy a FlowMeterInstance
*
* This is the destructor for the Tas.
*/
~FlowMeterInstance();
// Delete copy constructor and assignment operator to avoid misuse
FlowMeterInstance& operator=(const FlowMeterInstance&) = delete;
FlowMeterInstance(const FlowMeterInstance&) = delete;
void updateTokenBuckets();
bool Test(Ptr<Packet>);
protected:
private:
DataRate m_cir;
uint32_t m_cbs;
DataRate m_eir;
uint32_t m_ebs;
bool m_cf;
bool m_dropOnYellow;
bool m_markAllFramesRedEnable;
bool m_markAllFramesRed;
Time m_lastTokenUpdateTime;
double m_committedToken = 0;
double m_excessToken = 0;
};
}
#endif /* FLOW_METER_INSTANCE_H */

View File

@@ -0,0 +1,119 @@
#include "psfp-stream-filter-instance.h"
#include "ns3/log.h"
#include "ns3/integer.h"
#include "ns3/uinteger.h"
#include "ns3/boolean.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("StreamFilterInstance");
NS_OBJECT_ENSURE_REGISTERED(StreamFilterInstance);
TypeId
StreamFilterInstance::GetTypeId()
{
static TypeId tid =
TypeId("ns3::StreamFilterInstance")
.SetParent<Object>()
.SetGroupName("Tsn")
.AddConstructor<StreamFilterInstance>()
.AddAttribute("StreamHandle",
"Stream handle specification. -1 for wildcard",
IntegerValue(-1),
MakeIntegerAccessor(&StreamFilterInstance::m_streamHandle),
MakeIntegerChecker<int16_t>())
.AddAttribute("Priority",
"Priority specification. -1 for wildcard",
IntegerValue(-1),
MakeIntegerAccessor(&StreamFilterInstance::m_priority),
MakeIntegerChecker<int8_t>())
.AddAttribute("MaxSDUSize",
"Frames that exceed this SDU size do not pass the stream filter. 0 to disable it",
UintegerValue(0),
MakeUintegerAccessor(&StreamFilterInstance::m_maxSDUSize),
MakeUintegerChecker<uint16_t>())
.AddAttribute("StreamGateInstanceId",
"Stream Gate Instance ID. -1 for Stream Gate Instance",
IntegerValue(-1),
MakeIntegerAccessor(&StreamFilterInstance::m_streamGateInstanceId),
MakeIntegerChecker<int32_t>())
.AddAttribute("StreamBlockedDueToOversizeFrameEnable",
"StreamBlockedDueToOversizeFrame function activation boolean",
BooleanValue(false),
MakeBooleanAccessor(&StreamFilterInstance::m_streamBlockedDueToOversizeFrameEnable),
MakeBooleanChecker())
;
return tid;
}
StreamFilterInstance::StreamFilterInstance()
{
NS_LOG_FUNCTION(this);
m_streamBlockedDueToOversize = false;
}
StreamFilterInstance::~StreamFilterInstance()
{
NS_LOG_FUNCTION(this);
}
void
StreamFilterInstance::AddFlowMeterInstanceId(uint16_t flowMeterInstanceId)
{
NS_LOG_FUNCTION(this);
m_flowMeterInstanceIds.insert(m_flowMeterInstanceIds.end(),flowMeterInstanceId);
}
std::vector<uint16_t>
StreamFilterInstance::GetFlowMeterIds()
{
NS_LOG_FUNCTION(this);
return m_flowMeterInstanceIds;
}
bool
StreamFilterInstance::Match(uint16_t streamHandle, uint8_t priority){
NS_LOG_FUNCTION(this);
if (m_streamHandle < 0 || (m_streamHandle >= 0 && m_streamHandle == streamHandle)){
if (m_priority < 0 || (m_priority >= 0 && m_priority == priority)){
m_matchingFrameCount += 1;
return true;
}
}
return false;
}
bool
StreamFilterInstance::MaxSDUSizeFilter(Ptr<Packet> packet)
{
NS_LOG_FUNCTION(this);
if ((m_maxSDUSize > 0 and packet->GetSize() > m_maxSDUSize) or m_streamBlockedDueToOversize){
m_notPassingFrameCount += 1;
if (m_streamBlockedDueToOversizeFrameEnable and not m_streamBlockedDueToOversize){
m_streamBlockedDueToOversize = true;
}
return true;
}
else{
m_passingSDUCount += 1;
return false;
}
}
void
StreamFilterInstance::increaseRedFrameCount()
{
NS_LOG_FUNCTION(this);
m_redFrameCount += 1;
}
};

View File

@@ -0,0 +1,68 @@
#ifndef STREAM_FILTER_INSTANCE_H
#define STREAM_FILTER_INSTANCE_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/packet.h"
namespace ns3
{
class StreamFilterInstance: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a StreamFilterInstance
*/
StreamFilterInstance();
/**
* Destroy a StreamFilterInstance
*
* This is the destructor for the Tas.
*/
~StreamFilterInstance();
// Delete copy constructor and assignment operator to avoid misuse
StreamFilterInstance& operator=(const StreamFilterInstance&) = delete;
StreamFilterInstance(const StreamFilterInstance&) = delete;
void AddFlowMeterInstanceId(uint16_t flowMeterInstanceId);
std::vector<uint16_t> GetFlowMeterIds();
bool Match(uint16_t streamHandle, uint8_t priority);
bool MaxSDUSizeFilter(Ptr<Packet> packet);
void increaseRedFrameCount();
protected:
private:
int16_t m_streamHandle;
int8_t m_priority;
uint16_t m_maxSDUSize;
int32_t m_streamGateInstanceId;
std::vector<uint16_t> m_flowMeterInstanceIds ;
bool m_streamBlockedDueToOversizeFrameEnable;
bool m_streamBlockedDueToOversize;
uint32_t m_matchingFrameCount;
uint32_t m_passingFrameCount;
uint32_t m_notPassingFrameCount;
uint32_t m_passingSDUCount;
uint32_t m_notPassingSDUCount;
uint32_t m_redFrameCount;
};
}
#endif /* STREAM_FILTER_INSTANCE_H */

View File

@@ -0,0 +1,113 @@
#include "stream-identification-function-active-dest-mac-vlan.h"
#include "ns3/log.h"
#include "ns3/uinteger.h"
#include "ns3/mac48-address.h"
#include "ns3/packet.h"
#include "ns3/ethernet-trailer.h"
#include "stream-identification-function.h"
#include "ns3/ethernet-header2.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("ActiveDestMacVlanStreamIdentificationFunction");
NS_OBJECT_ENSURE_REGISTERED(ActiveDestMacVlanStreamIdentificationFunction);
TypeId
ActiveDestMacVlanStreamIdentificationFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::ActiveDestMacVlanStreamIdentificationFunction")
.SetParent<StreamIdentificationFunction>()
.SetGroupName("tsn")
.AddConstructor<ActiveDestMacVlanStreamIdentificationFunction>()
.AddAttribute("Address",
"Destination Mac Address",
AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")),
MakeAddressAccessor(&ActiveDestMacVlanStreamIdentificationFunction::m_destAddress),
MakeAddressChecker())
.AddAttribute("VlanID",
"Vlan ID",
UintegerValue(0),
MakeUintegerAccessor(&ActiveDestMacVlanStreamIdentificationFunction::m_vid),
MakeUintegerChecker<uint16_t>())
.AddAttribute("UpdateVlanID",
"Update Vlan ID",
UintegerValue(0),
MakeUintegerAccessor(&ActiveDestMacVlanStreamIdentificationFunction::m_updateVid),
MakeUintegerChecker<uint16_t>())
.AddAttribute("UpdateAddress",
"Update Destination Mac Address",
AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")),
MakeAddressAccessor(&ActiveDestMacVlanStreamIdentificationFunction::m_updateDestAddress),
MakeAddressChecker())
.AddAttribute("UpdatePCP",
"Update PCP",
UintegerValue(0),
MakeUintegerAccessor(&ActiveDestMacVlanStreamIdentificationFunction::m_updatePcp),
MakeUintegerChecker<uint8_t>());
return tid;
}
ActiveDestMacVlanStreamIdentificationFunction::ActiveDestMacVlanStreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
ActiveDestMacVlanStreamIdentificationFunction::~ActiveDestMacVlanStreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
bool
ActiveDestMacVlanStreamIdentificationFunction::Match(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
Ptr<Packet> pcopy = p->Copy();
EthernetHeader2 header;
pcopy->RemoveHeader(header);
if(m_destAddress==header.GetDest() && m_vid==header.GetVid())
{
return true;
}
return false;
}
void
ActiveDestMacVlanStreamIdentificationFunction::GetActiveUpdate(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
//Update the pkt
//Remove old header and trailer
EthernetHeader2 oldHeader;
p->RemoveHeader(oldHeader);
EthernetTrailer oldTrailer;
p->RemoveTrailer(oldTrailer);
//Add new header and trailer
EthernetHeader2 header;
header.SetDest(Mac48Address::ConvertFrom(m_updateDestAddress));
header.SetSrc(oldHeader.GetSrc());
header.SetEthertype(oldHeader.GetEthertype());
header.SetVlanTag(m_updatePcp, oldHeader.GetDei(), m_updateVid);
header.SetRedundancyTag(oldHeader.RemoveRedundancyTag());
p->AddHeader(header);
EthernetTrailer trailer;
trailer.EnableFcs(true);
trailer.CalcFcs(p);
p->AddTrailer(trailer);
}
}

View File

@@ -0,0 +1,56 @@
#ifndef ACTIVE_DEST_MAC_VLAN_STREAM_IDENTIFICATION_FUNCTION_H
#define ACTIVE_DEST_MAC_VLAN_STREAM_IDENTIFICATION_FUNCTION_H
#include "stream-identification-function.h"
#include "ns3/mac48-address.h"
#include "ns3/packet.h"
namespace ns3
{
class ActiveDestMacVlanStreamIdentificationFunction : public StreamIdentificationFunction
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a ActiveDestMacVlanStreamIdentificationFunction
*/
ActiveDestMacVlanStreamIdentificationFunction();
/**
* Destroy a ActiveDestMacVlanStreamIdentificationFunction
*
* This is the destructor for the ActiveDestMacVlanStreamIdentificationFunction.
*/
~ActiveDestMacVlanStreamIdentificationFunction();
// Delete copy constructor and assignment operator to avoid misuse
ActiveDestMacVlanStreamIdentificationFunction& operator=(const ActiveDestMacVlanStreamIdentificationFunction&) = delete;
ActiveDestMacVlanStreamIdentificationFunction(const ActiveDestMacVlanStreamIdentificationFunction&) = delete;
bool Match(Ptr<Packet> p) override;
void GetActiveUpdate(Ptr<Packet> p) override;
protected:
private:
Address m_destAddress;
uint16_t m_vid;
Address m_updateDestAddress;
uint16_t m_updateVid;
uint8_t m_updatePcp;
};
}
#endif /* ACTIVE_DEST_MAC_VLAN_STREAM_IDENTIFICATION_FUNCTION_H */

View File

@@ -0,0 +1,76 @@
#include "stream-identification-function-null.h"
#include "ns3/log.h"
#include "ns3/uinteger.h"
#include "ns3/mac48-address.h"
#include "ns3/packet.h"
#include "stream-identification-function.h"
#include "ns3/ethernet-header2.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("NullStreamIdentificationFunction");
NS_OBJECT_ENSURE_REGISTERED(NullStreamIdentificationFunction);
TypeId
NullStreamIdentificationFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::NullStreamIdentificationFunction")
.SetParent<StreamIdentificationFunction>()
.SetGroupName("tsn")
.AddConstructor<NullStreamIdentificationFunction>()
.AddAttribute("Address",
"Destination Mac Address",
AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")),
MakeAddressAccessor(&NullStreamIdentificationFunction::m_destAddress),
MakeAddressChecker())
.AddAttribute("VlanID",
"Vlan ID",
UintegerValue(0),
MakeUintegerAccessor(&NullStreamIdentificationFunction::m_vid),
MakeUintegerChecker<uint16_t>());
return tid;
}
NullStreamIdentificationFunction::NullStreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
NullStreamIdentificationFunction::~NullStreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
bool
NullStreamIdentificationFunction::Match(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
Ptr<Packet> pcopy = p->Copy();
EthernetHeader2 header;
pcopy->RemoveHeader(header);
if(m_destAddress==header.GetDest() && m_vid==header.GetVid())
{
return true;
}
return false;
}
void
NullStreamIdentificationFunction::GetActiveUpdate(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
//Do nothing because passive function
}
}

View File

@@ -0,0 +1,52 @@
#ifndef NULL_STREAM_IDENTIFICATION_FUNCTION_H
#define NULL_STREAM_IDENTIFICATION_FUNCTION_H
#include "stream-identification-function.h"
#include "ns3/mac48-address.h"
#include "ns3/packet.h"
namespace ns3
{
class NullStreamIdentificationFunction : public StreamIdentificationFunction
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a NullStreamIdentificationFunction
*/
NullStreamIdentificationFunction();
/**
* Destroy a NullStreamIdentificationFunction
*
* This is the destructor for the NullStreamIdentificationFunction.
*/
~NullStreamIdentificationFunction();
// Delete copy constructor and assignment operator to avoid misuse
NullStreamIdentificationFunction& operator=(const NullStreamIdentificationFunction&) = delete;
NullStreamIdentificationFunction(const NullStreamIdentificationFunction&) = delete;
bool Match(Ptr<Packet> p) override;
void GetActiveUpdate(Ptr<Packet> p) override;
protected:
private:
Address m_destAddress;
uint16_t m_vid;
};
}
#endif /* NULL_STREAM_IDENTIFICATION_FUNCTION_H */

View File

@@ -0,0 +1,67 @@
#include "stream-identification-function.h"
#include "ns3/log.h"
#include "ns3/uinteger.h"
#include "ns3/random-variable-stream.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("StreamIdentificationFunction");
NS_OBJECT_ENSURE_REGISTERED(StreamIdentificationFunction);
TypeId
StreamIdentificationFunction::GetTypeId()
{
static TypeId tid =
TypeId("ns3::StreamIdentificationFunction")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<StreamIdentificationFunction>()
.AddAttribute("MinLatencyOverhead",
"The minimum latency overhead cause by the identification function hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&StreamIdentificationFunction::m_minLatencyOverhead),
MakeTimeChecker())
.AddAttribute("MaxLatencyOverhead",
"The maximun latency overhead cause by the identification function hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&StreamIdentificationFunction::m_maxLatencyOverhead),
MakeTimeChecker());
return tid;
}
StreamIdentificationFunction::StreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
StreamIdentificationFunction::~StreamIdentificationFunction()
{
NS_LOG_FUNCTION(this);
}
bool
StreamIdentificationFunction::Match(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
return false;
}
void
StreamIdentificationFunction::GetActiveUpdate(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
}
Time
StreamIdentificationFunction::GetHardwareLatency()
{
NS_LOG_FUNCTION(this);
Ptr<UniformRandomVariable> randVar = CreateObject<UniformRandomVariable>();
return NanoSeconds(randVar->GetValue(m_minLatencyOverhead.GetNanoSeconds(), m_maxLatencyOverhead.GetNanoSeconds()));
}
}

View File

@@ -0,0 +1,53 @@
#ifndef STREAM_IDENTIFICATION_FUNCTION_H
#define STREAM_IDENTIFICATION_FUNCTION_H
#include "ns3/object.h"
#include "ns3/packet.h"
#include "ns3/nstime.h"
namespace ns3
{
class StreamIdentificationFunction : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a StreamIdentificationFunction
*/
StreamIdentificationFunction();
/**
* Destroy a StreamIdentificationFunction
*
* This is the destructor for the StreamIdentificationFunction.
*/
~StreamIdentificationFunction();
// Delete copy constructor and assignment operator to avoid misuse
StreamIdentificationFunction& operator=(const StreamIdentificationFunction&) = delete;
StreamIdentificationFunction(const StreamIdentificationFunction&) = delete;
virtual bool Match(Ptr<Packet> p);
virtual void GetActiveUpdate(Ptr<Packet> p);
Time GetHardwareLatency();
protected:
private:
Time m_minLatencyOverhead;
Time m_maxLatencyOverhead;
};
}
#endif /* STREAM_IDENTIFICATION_FUNCTION_H */

View File

@@ -0,0 +1,131 @@
#include "stream-identity-entry.h"
#include "ns3/log.h"
#include "ns3/packet.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("StreamIdEntry");
NS_OBJECT_ENSURE_REGISTERED(StreamIdEntry);
TypeId
StreamIdEntry::GetTypeId()
{
static TypeId tid =
TypeId("ns3::StreamIdEntry")
.SetParent<Object>()
.SetGroupName("tsn")
.AddConstructor<StreamIdEntry>()
.AddAttribute("StreamHandle",
"Stream Handle",
UintegerValue(1),
MakeUintegerAccessor(&StreamIdEntry::m_tsnStreamIdHandle),
MakeUintegerChecker<uint32_t>());
return tid;
}
StreamIdEntry::StreamIdEntry()
{
NS_LOG_FUNCTION(this);
}
StreamIdEntry::~StreamIdEntry()
{
NS_LOG_FUNCTION(this);
}
void
StreamIdEntry::SetStreamIdentificationFunction(Ptr<StreamIdentificationFunction> func)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdIdentificationFunction = func;
}
void
StreamIdEntry::SetOutFacInputPortList(std::vector<Ptr<TsnNetDevice>> portList)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdOutFacInputPortList = portList;
}
void
StreamIdEntry::SetInFacInputPortList(std::vector<Ptr<TsnNetDevice>> portList)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdInFacInputPortList = portList;
}
void
StreamIdEntry::SetInFacOutputPortList(std::vector<Ptr<TsnNetDevice>> portList)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdInFacOutputPortList = portList;
}
void
StreamIdEntry::SetOutFacOutputPortList(std::vector<Ptr<TsnNetDevice>> portList)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdOutFacOutputPortList = portList;
}
bool
StreamIdEntry::Match(Ptr<TsnNetDevice> net, bool infacing, bool input, Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
if (!infacing && input) //out-facing input
{
if (std::find(m_tsnStreamIdOutFacInputPortList.begin(), m_tsnStreamIdOutFacInputPortList.end(), net) != m_tsnStreamIdOutFacInputPortList.end())
{
return m_tsnStreamIdIdentificationFunction->Match(p);
}
}
else if(infacing && input)//in-facing input
{
if (std::find(m_tsnStreamIdInFacInputPortList.begin(), m_tsnStreamIdInFacInputPortList.end(), net) != m_tsnStreamIdInFacInputPortList.end())
{
return m_tsnStreamIdIdentificationFunction->Match(p);
}
}
else if(infacing && !input)//in-facing output
{
if (std::find(m_tsnStreamIdInFacOutputPortList.begin(), m_tsnStreamIdInFacOutputPortList.end(), net) != m_tsnStreamIdInFacOutputPortList.end())
{
return m_tsnStreamIdIdentificationFunction->Match(p);
}
}
else if(!infacing && !input)//out-facing output
{
if (std::find(m_tsnStreamIdOutFacOutputPortList.begin(), m_tsnStreamIdOutFacOutputPortList.end(), net) != m_tsnStreamIdOutFacOutputPortList.end())
{
return m_tsnStreamIdIdentificationFunction->Match(p);
}
}
return false;
}
uint32_t
StreamIdEntry::GetStreamHandle()
{
NS_LOG_FUNCTION(this);
return m_tsnStreamIdHandle;
}
void
StreamIdEntry::DoActiveUpdate(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
m_tsnStreamIdIdentificationFunction->GetActiveUpdate(p);
}
Time
StreamIdEntry::GetHardwareLatency()
{
NS_LOG_FUNCTION(this);
return m_tsnStreamIdIdentificationFunction->GetHardwareLatency();
}
}

View File

@@ -0,0 +1,67 @@
#ifndef STREAM_IDENTITY_ENTRY_H
#define STREAM_IDENTITY_ENTRY_H
#include "ns3/object.h"
#include "ns3/ptr.h"
#include "ns3/packet.h"
#include "tsn-net-device.h"
#include "stream-identification-function.h"
namespace ns3
{
class TsnNetDevice;
class StreamIdEntry : public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a StreamIdEntry
*/
StreamIdEntry();
/**
* Destroy a StreamIdEntry
*
* This is the destructor for the StreamIdEntry.
*/
~StreamIdEntry();
// Delete copy constructor and assignment operator to avoid misuse
StreamIdEntry& operator=(const StreamIdEntry&) = delete;
StreamIdEntry(const StreamIdEntry&) = delete;
void SetStreamIdentificationFunction(Ptr<StreamIdentificationFunction> func);
void SetOutFacInputPortList(std::vector<Ptr<TsnNetDevice>> portList);
void SetInFacInputPortList(std::vector<Ptr<TsnNetDevice>> portList);
void SetInFacOutputPortList(std::vector<Ptr<TsnNetDevice>> portList);
void SetOutFacOutputPortList(std::vector<Ptr<TsnNetDevice>> portList);
bool Match(Ptr<TsnNetDevice> net, bool infacing, bool input, Ptr<Packet> p);
uint32_t GetStreamHandle();
void DoActiveUpdate(Ptr<Packet> p);
Time GetHardwareLatency();
protected:
private:
uint32_t m_tsnStreamIdHandle;
std::vector<Ptr<TsnNetDevice>> m_tsnStreamIdInFacOutputPortList;
std::vector<Ptr<TsnNetDevice>> m_tsnStreamIdOutFacOutputPortList;
std::vector<Ptr<TsnNetDevice>> m_tsnStreamIdInFacInputPortList;
std::vector<Ptr<TsnNetDevice>> m_tsnStreamIdOutFacInputPortList;
Ptr<StreamIdentificationFunction> m_tsnStreamIdIdentificationFunction;
};
}
#endif /* STREAM_IDENTITY_ENTRY_H */

328
contrib/tsn/model/tas.cc Normal file
View File

@@ -0,0 +1,328 @@
#include "tas.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/data-rate.h"
#include "ns3/enum.h"
#include <bitset>
#include <math.h>
#include "ns3/random-variable-stream.h"
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-transmission-selection-algo.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("Tas");
NS_OBJECT_ENSURE_REGISTERED(Tas);
TypeId
Tas::GetTypeId()
{
static TypeId tid =
TypeId("ns3::Tas")
.SetParent<Object>()
.SetGroupName("Tsn")
.AddConstructor<Tas>()
.AddAttribute("GuardBandMode",
"Mode for the TAS guard band",
EnumValue(Tas::MTU),
MakeEnumAccessor(&Tas::m_GuardBandMode),
MakeEnumChecker(Tas::NONE, "NONE", Tas::MTU,"MTU", Tas::PKTSIZE, "PKTSIZE"))
.AddAttribute("MultidropMode",
"Mode for the 10Base-T1S",
BooleanValue(false),
MakeBooleanAccessor(&Tas::m_MultidropMode),
MakeBooleanChecker())
.AddTraceSource("GatesUpdate",
"Trace source indicating the current tas gate "
"states",
MakeTraceSourceAccessor(&Tas::m_gatesUpdate),
"ns3::TracedValueCallback::uint")
.AddAttribute("MaxGclEntryNumber",
"The maximum number of entry in the gate control list",
UintegerValue(65535),
MakeUintegerAccessor(&Tas::m_maxGclEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxGclCycleDuration",
"The maximum duration of the gate control list",
TimeValue(Seconds(65535)),
MakeTimeAccessor(&Tas::m_maxGclCycleDuration),
MakeTimeChecker())
.AddAttribute("MaxGclTimeInterval",
"The maximum time interval of a gate control list entry",
TimeValue(Seconds(65535)),
MakeTimeAccessor(&Tas::m_maxGclTimeInterval),
MakeTimeChecker())
.AddAttribute("MinLatencyOverhead",
"The minimum latency overhead cause by the TAS hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&Tas::m_minLatencyOverhead),
MakeTimeChecker())
.AddAttribute("MaxLatencyOverhead",
"The maximun latency overhead cause by the TAS hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&Tas::m_maxLatencyOverhead),
MakeTimeChecker());
return tid;
}
Tas::Tas()
{
NS_LOG_FUNCTION(this);
}
Tas::~Tas()
{
NS_LOG_FUNCTION(this);
}
void
Tas::SetTsnNetDevice(Ptr<TsnNetDevice> net)
{
NS_LOG_FUNCTION(this);
m_net = net;
GateUpdateCallback = MakeCallback(&TsnNetDevice::CheckForReadyPacket, m_net);
}
bool
Tas::IsEnable()
{
NS_LOG_FUNCTION(this);
if(m_GateControlList.size()==0)
{
return false;
}
else
{
return true;
}
}
void
Tas::AddTsa(Ptr<TsnTransmissionSelectionAlgo> tsa)
{
NS_LOG_FUNCTION(this);
m_transmissionSelectionAlgos.insert(m_transmissionSelectionAlgos.end(), tsa);
}
void
Tas::AddTransmissionGate()
{
NS_LOG_FUNCTION(this);
m_transmissionGates.insert(m_transmissionGates.end(), CreateObject<TransmissionGate>());
}
bool
Tas::IsGateOpen(int i)
{
NS_LOG_FUNCTION(this << i);
return m_transmissionGates[i]->IsOpen();
}
void
Tas::AddGclEntry(Time duration, uint8_t states)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_GateControlList.size() < m_maxGclEntryNumber, "Trying to add more GCL entry than the " << m_maxGclEntryNumber << " available.");
NS_ASSERT_MSG(duration <= m_maxGclTimeInterval, "Trying to add entry with longer time interval than the " << m_maxGclTimeInterval << " possible.");
NS_ASSERT_MSG(m_cycleDuration + duration <= m_maxGclCycleDuration, "Trying to create a longer TAS cycle than " << m_maxGclCycleDuration << " possible.");
GclEntry entry = {duration, states};
m_GateControlList.insert(m_GateControlList.end(), entry);
m_cycleDuration = m_cycleDuration + duration;
}
void
Tas::Start(){
NS_LOG_FUNCTION(this);
m_start = true;
UpdateGates(false);
}
void
Tas::ClockUpdate()
{
NS_LOG_FUNCTION(this);
UpdateGates(true);
}
void
Tas::UpdateGates(bool clockUpdate)
{
NS_LOG_FUNCTION(this);
if(m_GateControlList.size() == 0){ return;}
if(!m_start){ return;}
//Find the current gcl entry. Can't only do m_CurrentGclEntry = m_CurrentGclEntry + 1
//because synchronization and clock correction;
int oldCurrentGclEntry = m_CurrentGclEntry;
m_CurrentGclEntry = GetCurrentGclEntry();
GclEntry entry = m_GateControlList[m_CurrentGclEntry];
std::string binary = std::bitset<8>(entry.states).to_string();
std::reverse(binary.begin(), binary.end());
m_gatesUpdate(entry.states);
for (int i = 0; i < (int)binary.length(); i++){
std::string c{binary[i]};
int state = std::stoi(c);
if(state)
{
m_transmissionGates[i]->Open();
// NS_LOG_INFO(i << " Open");
}
else
{
m_transmissionGates[i]->Close();
// NS_LOG_INFO(i << " Close");
}
m_transmissionSelectionAlgos[i]->UpdateTransmissionGate(m_transmissionGates[i]->IsOpen());
}
//Compute in how much time the gates need to be update
Time delay = Time(0);
Ptr<Clock> activeClock = m_net->GetNode()->GetObject<TsnNode>()->GetActiveClock();
Time currentTime = activeClock->GetLocalTime();
if(!clockUpdate){
delay = activeClock->GetDuration(entry.duration); //Get duration to wait in the clock timebase
m_lastGateOpeningTime = currentTime;
}
else{
//Find the last gate opening time if the gcl entry change due to clock update
if(oldCurrentGclEntry != m_CurrentGclEntry)
{
m_lastGateOpeningTime = GetLastOpeningTime(currentTime);
}
delay = activeClock->GetDuration(entry.duration - (currentTime - m_lastGateOpeningTime));
}
Simulator::Cancel(m_NextGatesUpdate);
m_NextGatesUpdate = Simulator::Schedule(delay, &Tas::UpdateGates, this, false);
//To inform the TSN net device that the gate have change so it can check if a
//packet can be sent
if(m_CurrentGclEntry != oldCurrentGclEntry and !m_MultidropMode){
GateUpdateCallback();
}
}
int
Tas::GetCurrentGclEntry()
{
//Find the current position in the GCL according to the node clock
Time t = m_net->GetNode()->GetObject<TsnNode>()->GetActiveClock()->GetLocalTime();
t = (t-m_startTime)%m_cycleDuration;
Time duration = Time(0);
for(int i = 0; i<(int)m_GateControlList.size(); i++)
{
if (duration <= t && t< duration + m_GateControlList[i].duration)
{
return i;
}
duration = duration + m_GateControlList[i].duration;
}
NS_ASSERT_MSG(false, "Can't find current GCL entry");
return 0;
}
Time
Tas::GetLastOpeningTime(Time currentTime)
{
Time lastEntryTime = m_startTime + ((currentTime-m_startTime)/m_cycleDuration) * m_cycleDuration; //Last start of the cycle
for(int i = 0; i<(int)m_GateControlList.size(); i++)
{
if (lastEntryTime <= currentTime && currentTime< lastEntryTime + m_GateControlList[i].duration)
{
return lastEntryTime;
}
lastEntryTime = lastEntryTime + m_GateControlList[i].duration;
}
NS_ASSERT_MSG(false, "Can't find current GCL entry");
return Time(0);
}
bool
Tas::IsSendable(Ptr<const Packet> p, DataRate d, uint8_t preambleAndSFDGap, uint8_t interframeGap, uint32_t mtu)
{
NS_LOG_FUNCTION(this);
Ptr<Clock> clock = m_net->GetNode()->GetObject<TsnNode>()->GetActiveClock();
Time now =clock->GetLocalTime();
Time nextUpdate = Time(0);
if(Time::GetResolution()==Time::NS){
nextUpdate = clock->GetTimeAt(Time(NanoSeconds(m_NextGatesUpdate.GetTs())));
}
else if(Time::GetResolution()==Time::PS){
nextUpdate = clock->GetTimeAt(Time(PicoSeconds(m_NextGatesUpdate.GetTs())));
}
else{
NS_ASSERT_MSG(false, "TAS only support Time resolution in NS or PS.");
}
if(m_GuardBandMode==NONE)
{
return(true);
}
else if (m_GuardBandMode==PKTSIZE)
{
Time preambleAndSFDGapTime = d.CalculateBytesTxTime(preambleAndSFDGap);
Time txTime = d.CalculateBytesTxTime(p->GetSize());
Time interpacketGapTime = d.CalculateBytesTxTime(interframeGap);
Time txCompleteTime = preambleAndSFDGapTime + txTime + interpacketGapTime;
//Avoid guard band when schedule loop
//int entryId = GetCurrentGclEntry();
//uint8_t currentStates = m_GateControlList[entryId].states;
//int nextEntryId = entryId + 1;
//if (nextEntryId >= (int)m_GateControlList.size()){
// nextEntryId = 0;
//}
//uint8_t nextStates = m_GateControlList[nextEntryId].states;
//Time nextStatesDuration = m_GateControlList[nextEntryId].duration;
//End of "avoid guard band when shedule loop" modification
if(txCompleteTime <= nextUpdate -now)
{
return(true);
}
//Avoid guard band when schedule loop
//else if(nextStates == currentStates && txCompleteTime <= nextUpdate + nextStatesDuration - now)
//{
//If not enough time but next state is the same and enough time with the two combine slot ==> return True
//return(true);
//}
//End of "avoid guard band when shedule loop" modification
else
{
return(false);
}
}
else if (m_GuardBandMode==MTU)
{
if(d.CalculateBytesTxTime(mtu) <= nextUpdate -now)
{
return(true);
}
else
{
return(false);
}
}
return(false);
}
Time
Tas::GetHardwareLatency()
{
NS_LOG_FUNCTION(this);
Ptr<UniformRandomVariable> randVar = CreateObject<UniformRandomVariable>();
return NanoSeconds(randVar->GetValue(m_minLatencyOverhead.GetNanoSeconds(), m_maxLatencyOverhead.GetNanoSeconds()));
}
};

118
contrib/tsn/model/tas.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef TAS_H
#define TAS_H
#include "ns3/object.h"
#include "ns3/nstime.h"
#include "ns3/data-rate.h"
#include "ns3/tsn-net-device.h"
#include "ns3/transmission-gate.h"
#include "ns3/tsn-transmission-selection-algo.h"
namespace ns3
{
class TsnNetDevice;
class TsnTransmissionSelectionAlgo;
class Tas: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a Tas
*/
Tas();
/**
* Destroy a Tas
*
* This is the destructor for the Tas.
*/
~Tas();
// Delete copy constructor and assignment operator to avoid misuse
Tas& operator=(const Tas&) = delete;
Tas(const Tas&) = delete;
void SetTsnNetDevice(Ptr<TsnNetDevice> net);
bool IsEnable();
bool IsSendable(Ptr<const Packet> p, DataRate d, uint8_t preambleAndSFDGap, uint8_t interframeGap, uint32_t mtu);
void AddTsa(Ptr<TsnTransmissionSelectionAlgo> tsa);
void AddTransmissionGate();
void Start();
void ClockUpdate();
bool IsGateOpen(int i);
void AddGclEntry(Time duration, uint8_t states);
enum GuardBandModes
{
NONE,
MTU,
PKTSIZE,
};
Time GetHardwareLatency();
protected:
private:
void UpdateGates(bool clockUpdate);
int GetCurrentGclEntry();
Time GetLastOpeningTime(Time currentTime);
/**
* The TransmissionGates which this TsnNetDevice uses
*/
std::vector<Ptr<TransmissionGate>> m_transmissionGates;
Ptr<TsnNetDevice> m_net;
Callback<void> GateUpdateCallback;
/**
* \ingroup tsn
* Structure holding a gcl entry
*/
struct GclEntry
{
Time duration;
uint8_t states;
};
std::vector<Ptr<TsnTransmissionSelectionAlgo>> m_transmissionSelectionAlgos;
/**
* The GateControlList which this TsnNetDevice uses
*/
std::vector<GclEntry> m_GateControlList;
int m_CurrentGclEntry = 0;
Time m_cycleDuration;
Time m_startTime;
Time m_lastGateOpeningTime;
bool m_start = false;
EventId m_NextGatesUpdate;
bool m_MultidropMode;
GuardBandModes m_GuardBandMode;
TracedCallback<uint8_t> m_gatesUpdate;
uint16_t m_maxGclEntryNumber;
Time m_maxGclTimeInterval;
Time m_maxGclCycleDuration;
Time m_minLatencyOverhead;
Time m_maxLatencyOverhead;
};
}
#endif /* TAS_H */

View File

@@ -0,0 +1,65 @@
#include "transmission-gate.h"
#include "ns3/log.h"
#include "ns3/packet.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TransmissionGate");
NS_OBJECT_ENSURE_REGISTERED(TransmissionGate);
TypeId
TransmissionGate::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TransmissionGate")
.SetParent<Object>()
.SetGroupName("Tsn")
.AddConstructor<TransmissionGate>();
return tid;
}
TransmissionGate::TransmissionGate()
{
NS_LOG_FUNCTION(this);
m_state = OPEN;
}
TransmissionGate::~TransmissionGate()
{
NS_LOG_FUNCTION(this);
}
bool
TransmissionGate::IsOpen()
{
NS_LOG_FUNCTION(this);
if(m_state == OPEN)
{
return true;
}
else
{
return false;
}
}
void
TransmissionGate::Open()
{
m_state = OPEN;
}
void
TransmissionGate::Close()
{
m_state = CLOSE;
}
};

View File

@@ -0,0 +1,63 @@
#ifndef TRANSMISSION_GATE_H
#define TRANSMISSION_GATE_H
#include "ns3/object.h"
#include "ns3/packet.h"
namespace ns3
{
class TransmissionGate: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a TransmissionGate
*/
TransmissionGate();
/**
* Destroy a TransmissionGate
*
* This is the destructor for the TransmissionGate.
*/
~TransmissionGate();
// Delete copy constructor and assignment operator to avoid misuse
TransmissionGate& operator=(const TransmissionGate&) = delete;
TransmissionGate(const TransmissionGate&) = delete;
bool IsOpen();
void Open();
void Close();
protected:
private:
/**
* Enumeration of the states of the transimission gate.
*/
enum State
{
OPEN, /**< The transimission gate is open */
CLOSE /**< The transimission gate is close */
};
/**
* The state of the Net Device transmit state machine.
*/
State m_state;
};
}
#endif /* TRANSMISSION_GATE_H */

View File

@@ -0,0 +1,241 @@
#include "tsn-aggregated-net-device.h"
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-header2.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TsnAggregatedNetDevice");
NS_OBJECT_ENSURE_REGISTERED(TsnAggregatedNetDevice);
TypeId
TsnAggregatedNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnAggregatedNetDevice")
.SetParent<TsnNetDevice>()
.SetGroupName("Tsn")
.AddConstructor<TsnAggregatedNetDevice>();
return tid;
}
TsnAggregatedNetDevice::TsnAggregatedNetDevice()
{
NS_LOG_FUNCTION(this);
}
TsnAggregatedNetDevice::~TsnAggregatedNetDevice()
{
NS_LOG_FUNCTION(this);
}
void
TsnAggregatedNetDevice::AddNetDevice(Ptr<TsnNetDevice> net)
{
NS_LOG_FUNCTION(this);
net->SetPromiscReceiveCallback(MakeCallback(&TsnAggregatedNetDevice::MacRxPromiscCallback, this));
m_netDevices.insert(m_netDevices.end(),net);
}
void
TsnAggregatedNetDevice::DoDispose()
{
NS_LOG_FUNCTION(this);
m_netDevices.clear();
NetDevice::DoDispose();
}
bool
TsnAggregatedNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
//Pkt copy
Ptr<Packet> pkt = packet->Copy();
// Stream identification in-facing output
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, true, false, pkt)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
//FRER
// NS_LOG_INFO( "Stream handle of Pkt #"<<packet->GetUid() << " on output : " << streamHandle);
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
//TODO
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
//TODO
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
std::vector<Ptr<SequenceGenerationFunction>> seqGenTable = GetNode()->GetSequenceGenerationTable();
for (uint32_t i = 0; i < seqGenTable.size(); i++)
{
if(seqGenTable[i]->IsApplicable(streamHandle))
{
isSeqNumber = true;
seqNumber = seqGenTable[i]->GetSequenceNumber();
break;
}
}
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
if (isSeqNumber)
{
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, true))
{
seqEncTable[i]->Encode(pkt, seqNumber);
break;
}
}
}
break;
}
}
//Stream identification out-facing input (active)
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, true, pkt)){
streamIdentityTable[i]->DoActiveUpdate(pkt);
break;
}
}
//Send packet to the TsnNetDevice
bool res = true;
bool outRes = true;
for(int i = 0; i<(int)m_netDevices.size(); i++)
{
res = m_netDevices[i]->SendFrom(pkt, source, dest, ethertype);
if (!res){
outRes = false;
}
}
m_snifferTrace(pkt);
m_promiscSnifferTrace(pkt);
//Return false if one sendFrom have return false
return(outRes);
}
bool
TsnAggregatedNetDevice::MacRxPromiscCallback(Ptr<NetDevice> dev, Ptr<const Packet> p, uint16_t protocol, const Address& sender, const Address& receiver, PacketType pType)
{
NS_LOG_FUNCTION(this);
// 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(p);
m_promiscSnifferTrace(p);
m_phyRxEndTrace(p);
if (pType != PACKET_OTHERHOST)
{
//Pkt copy
Ptr<Packet> packet = p->Copy();
// Stream identification
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, true, packet)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
//FRER
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, true))
{
isSeqNumber = true;
seqNumber = seqEncTable[i]->Decode(packet);
break;
}
}
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
std::vector<Ptr<SequenceRecoveryFunction>> seqRcvyTable = GetNode()->GetSequenceRecoveryTable();
for (uint32_t i = 0; i < seqRcvyTable.size(); i++)
{
bool IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction, bool hasSeqNumber, bool individualRecovery);
if(seqRcvyTable[i]->IsApplicable(streamHandle, this, true, isSeqNumber, false))
{
if(!seqRcvyTable[i]->Recovery(seqNumber))
{
//Duplicate FRER packet detected ! ==> Drop
m_frerDropTrace(packet);
return false;
}
break;
}
}
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
//TODO
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
//TODO
break;
}
}
//A copy for callback
Ptr<Packet> originalPacket = packet->Copy();
//Hit the callback and trace
EthernetHeader2 header;
packet->RemoveHeader(header);
uint16_t protocol;
protocol = header.GetEthertype();
if (!m_promiscCallback.IsNull())
{
m_macPromiscRxTrace(originalPacket->Copy());
m_promiscCallback(this,
originalPacket,
protocol,
header.GetSrc(),
header.GetDest(),
pType);
}
//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_rxCallback(this, packet, protocol, header.GetSrc());
m_latencyTrace(originalPacket);
}
return true;
}
}

View File

@@ -0,0 +1,70 @@
#ifndef TSN_AGGREGATED_NET_DEVICE_H
#define TSN_AGGREGATED_NET_DEVICE_H
#include "ns3/tsn-net-device.h"
namespace ns3
{
/**
* \ingroup tsn
* \class TsnAggregatedNetDevice
* \brief A Device for aggregated Ethernet full duplex Link.
*
* This TsnAggregatedNetDevice class specializes the TsnAggregatedNetDevice
* class. This class don't follow any standard. It was designed for experiment
* 802.1CB features on end-station.
*/
class TsnAggregatedNetDevice : public TsnNetDevice
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* Construct a TsnAggregatedNetDevice
*
* This is the constructor for the TsnAggregatedNetDevice. It takes as a
* parameter a pointer to the Node to which this device is connected,
* as well as an optional DataRate object.
*/
TsnAggregatedNetDevice();
/**
* Destroy a TsnAggregatedNetDevice
*
* This is the destructor for the TsnAggregatedNetDevice.
*/
~TsnAggregatedNetDevice();
// Delete copy constructor and assignment operator to avoid misuse
TsnAggregatedNetDevice& operator=(const TsnAggregatedNetDevice&) = delete;
TsnAggregatedNetDevice(const TsnAggregatedNetDevice&) = delete;
void AddNetDevice(Ptr<TsnNetDevice>);
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype);
protected:
/**
* \brief Dispose of the object
*/
void DoDispose();
bool MacRxPromiscCallback(Ptr<NetDevice> dev, Ptr<const Packet> packet, uint16_t protocol, const Address& sender, const Address& receiver, PacketType pType);
std::vector<Ptr<TsnNetDevice>> m_netDevices;
};
}
#endif /* TSN_AGGREGATED_NET_DEVICE_H */

View File

@@ -0,0 +1,154 @@
#include "tsn-multidrop-channel.h"
#include "tsn-multidrop-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("TsnMultidropChannel");
NS_OBJECT_ENSURE_REGISTERED(TsnMultidropChannel);
TypeId
TsnMultidropChannel::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnMultidropChannel")
.SetParent<Channel>()
.SetGroupName("Tsn")
.AddConstructor<TsnMultidropChannel>()
.AddAttribute("Delay",
"Propagation delay through the channel",
TimeValue(NanoSeconds(25)),
MakeTimeAccessor(&TsnMultidropChannel::m_delay),
MakeTimeChecker())
.AddTraceSource("TxRxEthernet",
"Trace source indicating transmission of packet "
"from the TsnMultidropChannel, used by the Animation "
"interface.",
MakeTraceSourceAccessor(&TsnMultidropChannel::m_txrxEthernet),
"ns3::TsnMultidropChannel::TxRxAnimationCallback");
return tid;
}
TsnMultidropChannel::TsnMultidropChannel()
: Channel()
{
NS_LOG_FUNCTION_NOARGS();
}
void
TsnMultidropChannel::Attach(Ptr<TsnMultidropNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
NS_ASSERT(device);
NS_ASSERT(m_netDevices.size() < 255); //Limitation from IEEE802.3cg-2019
m_netDevices.insert(m_netDevices.end(), device);
//If we have two devices connected to the channel, the channel is usable
if (m_netDevices.size() >= 2)
{
m_wireState = IDLE;
}
}
bool
TsnMultidropChannel::TransmitStart(Ptr<const Packet> p, Ptr<TsnNetDevice> src, Time txTime)
{
NS_LOG_FUNCTION(this << p << src);
NS_LOG_LOGIC("UID is " << p->GetUid() << ")");
NS_ASSERT(m_wireState == IDLE);
for (std::size_t i = 0; i < m_netDevices.size(); ++i)
{
if(m_netDevices[i]!=src)
{
//Start of reception => Change PLCA state
m_netDevices[i]->StartReception();
//Schedule reception a the end of the transmission
Simulator::ScheduleWithContext(m_netDevices[i]->GetNode()->GetId(),
txTime + m_delay,
&TsnMultidropNetDevice::Receive,
m_netDevices[i],
p->Copy());
NS_LOG_INFO(txTime + m_delay);
}
}
// 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
TsnMultidropChannel::GetNDevices() const
{
NS_LOG_FUNCTION_NOARGS();
return m_netDevices.size();
}
Ptr<TsnNetDevice>
TsnMultidropChannel::GetEthernetDevice(std::size_t i) const
{
NS_LOG_FUNCTION_NOARGS();
NS_ASSERT(i < m_netDevices.size());
return m_netDevices[i];
}
Ptr<NetDevice>
TsnMultidropChannel::GetDevice(std::size_t i) const
{
NS_LOG_FUNCTION_NOARGS();
return GetEthernetDevice(i);
}
Time
TsnMultidropChannel::GetDelay() const
{
return m_delay;
}
bool
TsnMultidropChannel::IsInitialized() const
{
NS_ASSERT(m_wireState != INITIALIZING);
return true;
}
void
TsnMultidropChannel::StartBeaconTransmission()
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_wireState == IDLE);
m_wireState = TRANSMITTING;
for (std::size_t i = 0; i < m_netDevices.size(); ++i)
{
m_netDevices[i]->StartBeaconReception();
}
}
void
TsnMultidropChannel::StopBeaconTransmission()
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_wireState == TRANSMITTING);
m_wireState = IDLE;
for (std::size_t i = 0; i < m_netDevices.size(); ++i)
{
m_netDevices[i]->StopBeaconReception();
}
}
}

View File

@@ -0,0 +1,174 @@
#ifndef TSN_MULTIDROP_CHANNEL_H
#define TSN_MULTIDROP_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 TsnNetDevice;
class TsnMultidropNetDevice;
class Packet;
/**
* \ingroup ethernet
* \brief Ethernet Multidrop Channel.
*
* This class represents a half duplex multidrop Ethernet channel, called
* 10BASE-T1S, where the collision are avoid using a mechanism called PLCA
* (Physical Layer Collision Avoidance), implemented in the netDevice.
* This physical layer is proposed in IEEE 802.3cg-2019.
*
*/
class TsnMultidropChannel : public Channel
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a TsnMultidropChannel
*
* By default, you get a channel that has a 25ns delay no matter
* the netdevice.
*/
TsnMultidropChannel();
/**
* \brief Attach a given netdevice to this channel
* \param device pointer to the netdevice to attach to the channel
*/
void Attach(Ptr<TsnMultidropNetDevice> device);
/**
* \brief Transmit a packet over this channel
* \param p Packet to transmit
* \param src Source TsnNetDevice
* \param txTime Transmit time to apply
* \returns true if successful (currently always true)
*/
virtual bool TransmitStart(Ptr<const Packet> p, Ptr<TsnNetDevice> 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 TsnNetDevice corresponding to index i on this channel
* \param i Index number of the device requested
* \returns Ptr to TsnNetDevice requested
*/
Ptr<TsnNetDevice> 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;
/**
*
*/
void StartBeaconTransmission();
/**
*
*/
void StopBeaconTransmission();
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;
/**
* 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:
std::vector<Ptr<TsnMultidropNetDevice>> m_netDevices;
Time m_delay; //!< Propagation delay
/**
* 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
};
WireState m_wireState = INITIALIZING;
};
}
#endif /* TSN_MULTIDROP_CHANNEL_H */

View File

@@ -0,0 +1,758 @@
#include "tsn-multidrop-net-device.h"
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/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/timestamp-tag.h"
#include "ns3/ethernet-trailer.h"
#include "ns3/names.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TsnMultidropNetDevice");
NS_OBJECT_ENSURE_REGISTERED(TsnMultidropNetDevice);
TypeId
TsnMultidropNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnMultidropNetDevice")
.SetParent<TsnNetDevice>()
.SetGroupName("Tsn")
.AddConstructor<TsnMultidropNetDevice>()
.AddAttribute("PLCALocalNodeId",
"Local node ID for PLCA",
UintegerValue(0),
MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCALocalNodeId),
MakeUintegerChecker<uint8_t>())
.AddAttribute("PLCANodeCount",
"Number of node in the collision domain",
UintegerValue(8),
MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCANodeCount),
MakeUintegerChecker<uint8_t>())
.AddAttribute("PLCATransmitOpportunityTimer",
"Max number of bit time between transmit opportunity",
UintegerValue(32),
MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCATransmitOpportunityTimer),
MakeUintegerChecker<uint8_t>())
.AddAttribute("PLCAMaxBurstCount",
"Max number of additional packet that can be transmit in a single opportunity",
UintegerValue(0),
MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCAMaxBurstCount),
MakeUintegerChecker<uint8_t>())
.AddAttribute("PLCABurstTimer",
"Max number of bit time PLCA waits for the mac to send a "
"new packet before yielding the transmission opportunity",
UintegerValue(128),
MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCABurstTimer),
MakeUintegerChecker<uint8_t>())
.AddTraceSource("PLCAState",
"PLCA State trace",
MakeTraceSourceAccessor(&TsnMultidropNetDevice::m_PLCAStateTrace),
"ns3::TsnMultidropNetDevice::m_PLCAState");
return tid;
}
TsnMultidropNetDevice::TsnMultidropNetDevice()
{
NS_LOG_FUNCTION(this);
}
TsnMultidropNetDevice::~TsnMultidropNetDevice()
{
NS_LOG_FUNCTION(this);
}
void
TsnMultidropNetDevice::DoDispose()
{
NS_LOG_FUNCTION(this);
NetDevice::DoDispose();
}
bool
TsnMultidropNetDevice::Attach(Ptr<TsnMultidropChannel> 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();
//Init PLCA
PLCA();
return true;
}
bool
TsnMultidropNetDevice::TransmitStart(Ptr<Packet> p, int queue_id)
{
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 ");
//Timestamping at last bits emission
NS_ASSERT_MSG(m_node != nullptr, "m_node was not set using void TsnNetDevice::SetNode(Ptr<TsnNode> node)");
Time txTimestamp = m_node->GetTime();
//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");
NS_ASSERT_MSG(m_PLCAState == COMMIT, "Must be COMMIT to transmit with PLCA");
m_txMachineState = BUSY;
m_PLCAState = TRANSMIT;
m_txHardwareLatencyExperienced = false;
m_PLCAStateTrace(m_PLCAState);
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, &TsnTransmissionSelectionAlgo::TransmitComplete, m_transmissionSelectionAlgos[queue_id], p);
m_transmissionSelectionAlgos[queue_id]->TransmitStart(p, txTime);
Simulator::Schedule(txCompleteTime, &TsnMultidropNetDevice::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);
}
if(!m_rxCallbackWithTimestamp.IsNull()){
m_txCallbackWithTimestamp(p, txTimestamp);
}
return result;
}
bool
TsnMultidropNetDevice::isPacketPending()
{
int next_queue_id = TransmitSelection();
if(next_queue_id==-1)
{
return false;
}
else
{
return true;
}
}
void
TsnMultidropNetDevice::Send1Packet()
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_PLCAState == COMMIT);
int next_queue_id = TransmitSelection();
NS_ASSERT_MSG(next_queue_id!=-1, "No packet available");
NS_LOG_INFO("Send 1 pkt from queue " << (next_queue_id));
Ptr<Packet> p = m_queues[next_queue_id]->Dequeue();
m_snifferTrace(p);
m_promiscSnifferTrace(p);
TransmitStart(p, next_queue_id);
}
void
TsnMultidropNetDevice::TransmitComplete()
{
NS_LOG_FUNCTION(this);
// 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");
NS_ASSERT_MSG(m_PLCAState == TRANSMIT, "Must be COMMIT if transmitting with PLCA");
NS_LOG_INFO(Names::FindName(this) << " PKT #" << m_currentPkt->GetUid() << " at " << Simulator::Now().GetNanoSeconds()<<" Transmit end ");
m_txMachineState = READY;
NS_ASSERT_MSG(m_currentPkt, "TsnMultidropNetDevice::TransmitComplete(): m_currentPkt zero");
m_phyTxEndTrace(m_currentPkt);
//m_transmissionSelectionAlgos[queue_id]->TransmitComplete(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), &TsnMultidropNetDevice::PLCA, this);
}
void
TsnMultidropNetDevice::PLCA()
{
NS_LOG_FUNCTION(this);
NS_LOG_INFO(Names::FindName(this) << " : Current PLCA state = " << m_PLCAState);
if(m_PLCAState == DISABLE)
{
//We can't really turn on or off PLCA but we don't need this functionnality
//for our work
if(m_PLCALocalNodeId == 0)
{
m_PLCAactive = false;
m_PLCAState = RECOVER;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else
{
m_PLCAactive = false;
m_PLCAState = RESYNC;
m_PLCAStateTrace(m_PLCAState);
}
}
else if(m_PLCAState == RECOVER)
{
m_PLCAState = WAIT_TO;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else if(m_PLCAState == RESYNC)
{
if(m_PLCALocalNodeId == 0)
{
m_PLCAactive = true;
m_PLCAState = SEND_BEACON;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
}
else if (m_PLCAState == SYNCING)
{
m_PLCACurID = 0;
m_PLCAState = WAIT_TO;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else if(m_PLCAState == SEND_BEACON)
{
m_PLCAactive = true;
m_channel->StartBeaconTransmission();
Simulator::Schedule(m_bps.CalculateBytesTxTime(m_PLCABeaconTimer), &TsnMultidropNetDevice::BeaconTimerDone, this);
}
else if(m_PLCAState == WAIT_TO)
{
m_PLCAToTimerEvent = Simulator::Schedule(m_bps.CalculateBytesTxTime(m_PLCATransmitOpportunityTimer), &TsnMultidropNetDevice::ToTimerDone, this);
if(m_PLCALocalNodeId == m_PLCACurID)
{
if (isPacketPending())
{
m_PLCAState = COMMIT;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else
{
m_PLCAState = YIELD;
m_PLCAStateTrace(m_PLCAState);
}
}
}
else if (m_PLCAState == COMMIT)
{
Simulator::Cancel(m_PLCAToTimerEvent);
m_PLCABurstCount = 0;
Simulator::Schedule(Time(0), &TsnMultidropNetDevice::Send1Packet, this); //Schedule in 0 to put this at the end of the event list
}
else if (m_PLCAState == TRANSMIT)
{
//At least one pkt is ready to be sent
if(m_txMachineState == READY)
{
if (m_PLCABurstCount >= m_PLCAMaxBurstCount)
{
m_PLCAState = NEXT_TX_OPPORTUNITY;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else
{
m_PLCAState = BURST;
m_PLCAStateTrace(m_PLCAState);
NS_ASSERT_MSG(false, "PLCA burst mode not implemented");
PLCA();
}
}
}
else if (m_PLCAState == BURST)
{
m_PLCABurstCount = m_PLCABurstCount + 1;
if (isPacketPending())
{
Send1Packet();
m_PLCAState = TRANSMIT;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
else
{
//TODO burst timer ! How does it work ????
}
}
else if (m_PLCAState == YIELD)
{
//Do nothing
}
else if (m_PLCAState == NEXT_TX_OPPORTUNITY)
{
m_PLCACurID = m_PLCACurID + 1;
NS_LOG_INFO(Names::FindName(this) << " : New PLCACurId = " << (uint16_t) m_PLCACurID);
if (m_PLCALocalNodeId == 0 && m_PLCACurID >= m_PLCANodeCount)
{
m_PLCAState = RESYNC;
}
else
{
m_PLCAState = WAIT_TO;
}
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
}
void
TsnMultidropNetDevice::ToTimerDone()
{
NS_LOG_FUNCTION(this);
m_PLCAState = NEXT_TX_OPPORTUNITY;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
void
TsnMultidropNetDevice::BeaconTimerDone()
{
NS_LOG_FUNCTION(this);
m_PLCAState = SYNCING;
m_PLCAStateTrace(m_PLCAState);
m_channel->StopBeaconTransmission();
PLCA();
}
void
TsnMultidropNetDevice::BeaconDetTimerDone()
{
NS_LOG_FUNCTION(this);
m_PLCAState = RESYNC;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
void
TsnMultidropNetDevice::StartBeaconReception()
{
NS_LOG_FUNCTION(this);
if (m_PLCALocalNodeId != 0)
{
m_PLCAState = EARLY_RECEIVE;
m_PLCAStateTrace(m_PLCAState);
Simulator::Cancel(m_PLCAToTimerEvent);
m_PLCABeaconDetTimerEvent = Simulator::Schedule(m_bps.CalculateBytesTxTime(m_PLCABeaconDetTimer), &TsnMultidropNetDevice::BeaconDetTimerDone, this);
}
}
void
TsnMultidropNetDevice::StopBeaconReception()
{
NS_LOG_FUNCTION(this);
if (m_PLCALocalNodeId != 0)
{
if (m_PLCABeaconDetTimerEvent.IsRunning())
{
Simulator::Cancel(m_PLCABeaconDetTimerEvent);
m_PLCAState = SYNCING;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
}
}
void
TsnMultidropNetDevice::StartReception()
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_PLCAState == WAIT_TO || m_PLCAState == RECEIVE, "m_PLCAState = " << m_PLCAState);
if(m_PLCAState == WAIT_TO)
{
m_PLCAState = EARLY_RECEIVE;
m_PLCAStateTrace(m_PLCAState);
Simulator::Cancel(m_PLCAToTimerEvent);
m_PLCAState = RECEIVE; //The early receive state is jump because we don't simulate the phy layer
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
}
void
TsnMultidropNetDevice::EndReception()
{
NS_LOG_FUNCTION(this);
m_PLCAState = NEXT_TX_OPPORTUNITY;
m_PLCAStateTrace(m_PLCAState);
PLCA();
}
void
TsnMultidropNetDevice::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 = header.GetEthertype();
int8_t priority = header.GetPcp();
// Stream identification out-facing input
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint32_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, true, originalPacket)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
//PSFP
std::vector<Ptr<StreamFilterInstance>> streamFilterInstanceTable = GetNode()->GetStreamFilterInstanceTable();
for (uint16_t i = 0; i < streamFilterInstanceTable.size(); i++){
if (streamFilterInstanceTable[i]->Match(streamHandle, priority)){
//MaxSDUSizeFilter
if (streamFilterInstanceTable[i]->MaxSDUSizeFilter(originalPacket)){
m_maxSDUSizeFilterDropTrace(originalPacket);
return;
}
//TODO Stream Gate
//Flow Meter
std::vector<uint16_t> fmids = streamFilterInstanceTable[i]->GetFlowMeterIds();
std::vector<Ptr<FlowMeterInstance>> flowMeterInstanceTable = GetNode()->GetFlowMeterInstanceTable();
bool pass = true;
for (uint16_t j = 0; j < fmids.size(); j++){
if(flowMeterInstanceTable[fmids[j]]->Test(originalPacket)){
pass = false;
}
}
if (not pass){
streamFilterInstanceTable[i]->increaseRedFrameCount();
m_REDFrameDropTrace(originalPacket);
return;
}
break;
}
}
//FRER
// NS_LOG_INFO( "Stream handle of Pkt #"<<packet->GetUid() << " on input : " << streamHandle);
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
//TODO
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
//TODO
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
std::vector<Ptr<SequenceGenerationFunction>> seqGenTable = GetNode()->GetSequenceGenerationTable();
for (uint32_t i = 0; i < seqGenTable.size(); i++)
{
if(seqGenTable[i]->IsApplicable(streamHandle))
{
isSeqNumber = true;
seqNumber = seqGenTable[i]->GetSequenceNumber();
break;
}
}
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
if (isSeqNumber)
{
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, false))
{
seqEncTable[i]->Encode(originalPacket, seqNumber);
break;
}
}
}
break;
}
}
//
// 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);
}
}
Simulator::Schedule(m_bps.CalculateBytesTxTime(m_interframeGap), &TsnMultidropNetDevice::EndReception, this);
}
bool
TsnMultidropNetDevice::IsPointToPoint() const
{
NS_LOG_FUNCTION(this);
return false;
}
bool
TsnMultidropNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
// Stream identification in-facing output
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, true, false, packet)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
//FRER
// NS_LOG_INFO( "Stream handle of Pkt #"<<packet->GetUid() << " on output : " << streamHandle);
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, false))
{
isSeqNumber = true;
seqNumber = seqEncTable[i]->Decode(packet);
break;
}
}
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
std::vector<Ptr<SequenceRecoveryFunction>> seqRcvyTable = GetNode()->GetSequenceRecoveryTable();
for (uint32_t i = 0; i < seqRcvyTable.size(); i++)
{
bool IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction, bool hasSeqNumber, bool individualRecovery);
if(seqRcvyTable[i]->IsApplicable(streamHandle, this, false, isSeqNumber, false))
{
if(!seqRcvyTable[i]->Recovery(seqNumber))
{
//Duplicate FRER packet detected ! ==> Drop
m_frerDropTrace(packet);
return false;
}
break;
}
}
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
//TODO
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
//TODO
break;
}
}
//Stream identification out-facing output (active)
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, false, packet)){
streamIdentityTable[i]->DoActiveUpdate(packet);
break;
}
}
//
// 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();
m_FIFOStateSnifferTrace(m_queues, m_txMachineState==BUSY, packet);
//NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Enqueue pkt #"<<packet->GetUid() << " (Vid : " << vid << ") in FIFO #" << unsigned(pcp));
//
// 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), &TsnNetDevice::CheckForReadyPacket, this);
return true;
}
// Enqueue may fail (overflow)
m_macTxDropTrace(packet);
return false;
}
}

View File

@@ -0,0 +1,228 @@
#ifndef TSN_MULTIDROP_NET_DEVICE_H
#define TSN_MULTIDROP_NET_DEVICE_H
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
namespace ns3
{
/**
* \ingroup tsn
* \class TsnMultidropNetDevice
* \brief A Device for 10Base-T1S half duplex Link with PLCA.
*
* This TsnMultidropNetDevice class specializes the TsnNetDevice
* class to simulate 10Base-T1S NIC with PLCA
*
*/
class TsnMultidropNetDevice : public TsnNetDevice
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* Construct a TsnMultidropNetDevice
*
* This is the constructor for the TsnMultidropNetDevice. It takes as a
* parameter a pointer to the Node to which this device is connected,
* as well as an optional DataRate object.
*/
TsnMultidropNetDevice();
/**
* Destroy a TsnMultidropNetDevice
*
* This is the destructor for the TsnMultidropNetDevice.
*/
~TsnMultidropNetDevice();
// Delete copy constructor and assignment operator to avoid misuse
TsnMultidropNetDevice& operator=(const TsnMultidropNetDevice&) = delete;
TsnMultidropNetDevice(const TsnMultidropNetDevice&) = delete;
/**
* 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<TsnMultidropChannel> ch);
bool IsPointToPoint() const override;
/**
* Receive a packet from a connected EthernetMultidropNetDevice.
*
* The EthernetMultidropNetDevice 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.
*/
void Receive(Ptr<Packet> p) override;
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype) override;
void Send1Packet();
/**
*
*/
void StartBeaconReception();
/**
*
*/
void StopBeaconReception();
/**
*
*/
void StartReception();
/**
*
*
*/
void EndReception();
enum PLCAState
{
/** Disable state */
DISABLE,
/** Recover state */
RECOVER,
/** Resync state */
RESYNC,
/** Send beacon state*/
SEND_BEACON,
/** Syncing state*/
SYNCING,
/** Wait transmit opportunity state*/
WAIT_TO,
/** Commit state*/
COMMIT,
/** Transmit state*/
TRANSMIT,
/** Burst state*/
BURST,
/** Receive state*/
RECEIVE,
/** Yield state*/
YIELD,
/** Abort state*/
ABORT,
/** Next tx opportunity state*/
NEXT_TX_OPPORTUNITY,
/** Early receive state*/
EARLY_RECEIVE,
};
protected:
/**
* \brief Dispose of the object
*/
void DoDispose();
/**
* 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, int queue_id);
/**
* 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();
/**
*
*/
bool isPacketPending();
/**
*
*/
void PLCA();
/**
*
*/
void ToTimerDone();
/**
*
*/
void BeaconTimerDone();
/**
*
*/
void BeaconDetTimerDone();
/**
* The TsnMultidropChannel to which this TsnMultidropNetDevice has
* been attached.
*/
Ptr<TsnMultidropChannel> m_channel;
uint8_t m_PLCALocalNodeId;
uint8_t m_PLCACurID = 0;
uint8_t m_PLCANodeCount;
uint8_t m_PLCATransmitOpportunityTimer;
uint8_t m_PLCABurstCount = 0;
uint8_t m_PLCAMaxBurstCount;
uint8_t m_PLCABurstTimer;
uint8_t m_PLCABeaconTimer = 20;
uint8_t m_PLCABeaconDetTimer = 22;
bool m_PLCAactive = false;
PLCAState m_PLCAState = DISABLE;
TracedCallback<PLCAState> m_PLCAStateTrace;
EventId m_PLCAToTimerEvent;
EventId m_PLCABeaconDetTimerEvent;
DataRate m_bps = DataRate("10Mb/s");
};
}
#endif /* TSN_MULTIDROP_NET_DEVICE_H */

View File

@@ -0,0 +1,694 @@
#include "tsn-net-device.h"
#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/queue.h"
#include "ns3/ethernet-trailer.h"
#include "ns3/names.h"
#include "ns3/timestamp-tag.h"
#include "ns3/simulator.h"
#include "ns3/callback.h"
#include "ns3/error-model.h"
#include "ns3/ethernet-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-header2.h"
#include "ns3/tsn-transmission-selection-algo.h"
#include "ns3/tas.h"
#include "ns3/transmission-gate.h"
#include "psfp-stream-filter-instance.h"
#include "psfp-flow-meter-instance.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TsnNetDevice");
NS_OBJECT_ENSURE_REGISTERED(TsnNetDevice);
TypeId
TsnNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnNetDevice")
.SetParent<EthernetNetDevice>()
.SetGroupName("Tsn")
.AddConstructor<TsnNetDevice>()
.AddTraceSource("FrerDrop",
"Trace source indicating a packet was drop "
"by FRER recovery function",
MakeTraceSourceAccessor(&TsnNetDevice::m_frerDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MaxSDUSizeFilterDrop",
"Trace source indicating a packet was drop "
"by the maxSDUSize filter",
MakeTraceSourceAccessor(&TsnNetDevice::m_maxSDUSizeFilterDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("REDFrameDrop",
"Trace source indicating a packet was drop "
"by the flow meter",
MakeTraceSourceAccessor(&TsnNetDevice::m_REDFrameDropTrace),
"ns3::Packet::TracedCallback");
return tid;
}
TsnNetDevice::TsnNetDevice(): EthernetNetDevice()
{
NS_LOG_FUNCTION(this);
m_tas = CreateObject<Tas>();
m_tas->SetTsnNetDevice(this);
}
TsnNetDevice::~TsnNetDevice()
{
NS_LOG_FUNCTION(this);
}
void
TsnNetDevice::SetNode(Ptr<TsnNode> node)
{
m_node = node;
}
void
TsnNetDevice::SetQueue(Ptr<Queue<Packet>> q)
{
NS_LOG_FUNCTION(this << q);
Ptr<TsnTransmissionSelectionAlgo> tsa = CreateObject<TsnTransmissionSelectionAlgo>();
tsa->SetQueue(q);
tsa->SetTsnNetDevice(this);
SetQueue(q, tsa);
}
void
TsnNetDevice::SetQueue(Ptr<Queue<Packet>> q, Ptr<TsnTransmissionSelectionAlgo> tsa)
{
NS_LOG_FUNCTION(this << q);
tsa->SetQueue(q);
m_queues.insert(m_queues.end(),q);
m_tas->AddTransmissionGate();
m_tas->AddTsa(tsa);
m_transmissionSelectionAlgos.insert(m_transmissionSelectionAlgos.end(), tsa);
}
void
TsnNetDevice::UpdateQueue(uint32_t queue_id, Ptr<Queue<Packet>> q, Ptr<TsnTransmissionSelectionAlgo> tsa)
{
NS_LOG_FUNCTION(this << q);
NS_ASSERT(queue_id < m_queues.size());
NS_ASSERT(queue_id < m_transmissionSelectionAlgos.size());
tsa->SetQueue(q);
m_tas->AddTransmissionGate();
m_tas->AddTsa(tsa);
m_queues[queue_id] = q;
m_transmissionSelectionAlgos[queue_id] = tsa;
}
int
TsnNetDevice::TransmitSelection()
{
NS_LOG_FUNCTION(this);
for (int i = m_queues.size(); i --> 0; )
{
if(!m_queues[i]->IsEmpty() && m_tas->IsGateOpen(i))
{
if (m_transmissionSelectionAlgos[i]->IsReadyToTransmit())
{
if(m_tas->IsEnable())
{
Ptr<const Packet> p = m_queues[i]->Peek();
if(m_tas->IsSendable(p, m_bps, m_preambleAndSFDGap, m_interframeGap, m_mtu))
{
return(i);
}
}
else
{
return(i);
}
}
}
}
return(-1);
}
bool
TsnNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
Time hardwareLatency = Time(0);
// Stream identification in-facing output
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, true, false, packet)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
hardwareLatency += streamIdentityTable[i]->GetHardwareLatency();
//FRER
// NS_LOG_INFO( "Stream handle of Pkt #"<<packet->GetUid() << " on output : " << streamHandle);
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, false))
{
isSeqNumber = true;
seqNumber = seqEncTable[i]->Decode(packet);
break;
}
}
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
std::vector<Ptr<SequenceRecoveryFunction>> seqRcvyTable = GetNode()->GetSequenceRecoveryTable();
for (uint32_t i = 0; i < seqRcvyTable.size(); i++)
{
bool IsApplicable(uint32_t streamHandle, Ptr<TsnNetDevice> port, bool direction, bool hasSeqNumber, bool individualRecovery);
if(seqRcvyTable[i]->IsApplicable(streamHandle, this, false, isSeqNumber, false))
{
if(!seqRcvyTable[i]->Recovery(seqNumber))
{
//Duplicate FRER packet detected ! ==> Drop
m_frerDropTrace(packet);
return false;
}
hardwareLatency += seqRcvyTable[i]->GetHardwareLatency();
break;
}
}
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
//TODO
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
//TODO
break;
}
}
//Stream identification out-facing output (active)
for (uint16_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, false, packet)){
streamIdentityTable[i]->DoActiveUpdate(packet);
hardwareLatency += streamIdentityTable[i]->GetHardwareLatency();
break;
}
}
//
// 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();
m_FIFOStateSnifferTrace(m_queues, m_txMachineState==BUSY, packet);
// NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Enqueue pkt #"<<packet->GetUid() << " (Vid : " << header.GetVid() << ") in FIFO #" << unsigned(pcp));
//
// We should enqueue and dequeue the packet to hit the tracing hooks.
//
if (m_queues[pcp]->Enqueue(packet))
{
//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(hardwareLatency, &TsnNetDevice::CheckForReadyPacket, this);
//NS_LOG_INFO(Names::FindName(this) << " Enqueue pkt #"<<packet->GetUid() << " OK !");
return true;
}
// Enqueue may fail (overflow)
m_macTxDropTrace(packet);
return false;
}
bool
TsnNetDevice::SendWithSpecificFIFO(Ptr<Packet> packet, const Address& dest, uint16_t ethertype, uint8_t pcp)
{
NS_LOG_FUNCTION(this);
return SendFrom(packet, GetAddress(), dest, ethertype, pcp);
}
bool
TsnNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype,
uint8_t pcp)
{
// This function is use to put GPTP frame in a specific FIFO without using QTAG
NS_LOG_FUNCTION(this);
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()));
AddHeader(packet, Mac48Address::ConvertFrom(dest), Mac48Address::ConvertFrom(source), ethertype);
EthernetTrailer trailer;
trailer.EnableFcs(true);
trailer.CalcFcs(packet);
packet->AddTrailer(trailer);
//
// 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_FIFOStateSnifferTrace(m_queues, m_txMachineState==BUSY, packet);
// NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Enqueue pkt #"<<packet->GetUid() << " in FIFO #" << unsigned(pcp));
//
// 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), &TsnNetDevice::CheckForReadyPacket, this);
return true;
}
// Enqueue may fail (overflow)
m_macTxDropTrace(packet);
return false;
}
bool
TsnNetDevice::TransmitStart(Ptr<Packet> p, int queue_id)
{
NS_LOG_FUNCTION(this << p);
NS_LOG_LOGIC("UID is " << p->GetUid() << ")");
//Timestamping at last bits emission
NS_ASSERT_MSG(m_node != nullptr, "m_node was not set using void TsnNetDevice::SetNode(Ptr<TsnNode> node)");
Time txTimestamp = m_node->GetTime();
// NS_LOG_INFO("On " << Names::FindName(this)<< " Tx start at " << Simulator::Now().GetNanoSeconds() << " recording : " << txTimestamp.GetNanoSeconds());
//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_txHardwareLatencyExperienced = false;
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());
Simulator::Schedule(txCompleteTime, &TsnTransmissionSelectionAlgo::TransmitComplete, m_transmissionSelectionAlgos[queue_id], p);
m_transmissionSelectionAlgos[queue_id]->TransmitStart(p, txTime);
NS_LOG_LOGIC("Schedule TransmitCompleteEvent in " << txCompleteTime.As(Time::S));
Simulator::Schedule(txCompleteTime, &TsnNetDevice::TransmitComplete, this, queue_id);
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);
}
if(!m_rxCallbackWithTimestamp.IsNull()){
m_txCallbackWithTimestamp(p, txTimestamp);
}
return result;
}
void
TsnNetDevice::TransmitComplete(int queue_id)
{
NS_LOG_FUNCTION(this);
// 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, "TsnNetDevice::TransmitComplete(): m_currentPkt zero");
m_phyTxEndTrace(m_currentPkt);
//m_transmissionSelectionAlgos[queue_id]->TransmitComplete(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), &TsnNetDevice::CheckForReadyPacket, this);
}
void
TsnNetDevice::CheckForReadyPacket()
{
NS_LOG_FUNCTION(this);
if (m_txMachineState != READY || m_txHardwareLatencyExperienced)
{
return;
}
int next_queue_id = TransmitSelection();
if(next_queue_id==-1)
{
NS_LOG_LOGIC("No pending packets in device queue after tx complete");
return;
}
//
// Got another packet off of the queue, so start the transmit process again.
//
Ptr<Packet> p = m_queues[next_queue_id]->Dequeue();
m_snifferTrace(p);
m_promiscSnifferTrace(p);
m_txHardwareLatencyExperienced = true;
Time hardwareLatency = m_transmissionSelectionAlgos[next_queue_id]->GetHardwareLatency() + m_tas->GetHardwareLatency();
Simulator::Schedule(hardwareLatency, &TsnNetDevice::TransmitStart, this, p, next_queue_id);
}
void
TsnNetDevice::Receive(Ptr<Packet> packet)
{
NS_LOG_FUNCTION(this << packet);
NS_LOG_LOGIC("UID is " << packet->GetUid());
//Timestamping at last bits receiption thus compensate for the duration between first bit and last
NS_ASSERT_MSG(m_node != nullptr, "m_node was not set using void TsnNetDevice::SetNode(Ptr<TsnNode> node)");
Time rxTimestamp = m_node->GetTime() - (m_bps.CalculateBytesTxTime(m_preambleAndSFDGap) + m_bps.CalculateBytesTxTime(packet->GetSize()));
Time hardwareLatency = Time(0);
// NS_LOG_INFO("On " << Names::FindName(this)<< " Rx end at " << (Simulator::Now()- (m_bps.CalculateBytesTxTime(m_preambleAndSFDGap) + m_bps.CalculateBytesTxTime(packet->GetSize()))).GetNanoSeconds() << " recording : " << rxTimestamp.GetNanoSeconds());
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();
//Check checksum
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 = header.GetEthertype();
uint8_t priority = header.GetPcp();
// Stream identification out-facing input
std::vector<Ptr<StreamIdEntry>> streamIdentityTable = GetNode()->GetStreamIdentityTable();
for (uint32_t i = 0; i < streamIdentityTable.size(); i++){
if (streamIdentityTable[i]->Match(this, false, true, originalPacket)){
uint32_t streamHandle = streamIdentityTable[i]->GetStreamHandle();
hardwareLatency += streamIdentityTable[i]->GetHardwareLatency();
//PSFP
std::vector<Ptr<StreamFilterInstance>> streamFilterInstanceTable = GetNode()->GetStreamFilterInstanceTable();
for (uint16_t i = 0; i < streamFilterInstanceTable.size(); i++){
if (streamFilterInstanceTable[i]->Match(streamHandle, priority)){
//MaxSDUSizeFilter
if (streamFilterInstanceTable[i]->MaxSDUSizeFilter(originalPacket)){
m_maxSDUSizeFilterDropTrace(originalPacket);
return;
}
//TODO Stream Gate
//Flow Meter
std::vector<uint16_t> fmids = streamFilterInstanceTable[i]->GetFlowMeterIds();
std::vector<Ptr<FlowMeterInstance>> flowMeterInstanceTable = GetNode()->GetFlowMeterInstanceTable();
bool pass = true;
for (uint16_t j = 0; j < fmids.size(); j++){
if(flowMeterInstanceTable[fmids[j]]->Test(originalPacket)){
pass = false;
}
}
if (not pass){
streamFilterInstanceTable[i]->increaseRedFrameCount();
m_REDFrameDropTrace(originalPacket);
return;
}
break;
}
}
//FRER
// NS_LOG_INFO( "Stream handle of Pkt #"<<packet->GetUid() << " on input : " << streamHandle);
bool isSeqNumber = false;
uint16_t seqNumber = 0;
//Sequence decode => extract RTAG if this function is enable for this stream handle
//TODO
//Individual Recovery if this function is enable for this stream handle
//TODO
//Sequence Recovery if this function is enable for this stream handle
//TODO
//Stream Transfert Function
//Nothing to implement in the simulator
//Sequence Generation if this function is enable for this stream handle
std::vector<Ptr<SequenceGenerationFunction>> seqGenTable = GetNode()->GetSequenceGenerationTable();
for (uint32_t i = 0; i < seqGenTable.size(); i++)
{
if(seqGenTable[i]->IsApplicable(streamHandle))
{
isSeqNumber = true;
seqNumber = seqGenTable[i]->GetSequenceNumber();
break;
}
}
//Stream splitting if this function is enable for this stream handle
//TODO
//Sequence encode => encode RTAG if this function is enable for this stream handle
if (isSeqNumber)
{
std::vector<Ptr<SequenceEncodeDecodeFunction>> seqEncTable = GetNode()->GetSequenceEncodeDecodeTable();
for (uint32_t i = 0; i < seqEncTable.size(); i++)
{
if(seqEncTable[i]->IsApplicable(streamHandle, this, false))
{
seqEncTable[i]->Encode(originalPacket, seqNumber);
break;
}
}
}
break;
}
}
//
// Classify the packet based on its destination.
//
PacketType packetType;
if (header.GetDest() == m_address)
{
packetType = PACKET_HOST;
}
else if (header.GetDest().IsBroadcast())
{
packetType = PACKET_BROADCAST;
}
else if (header.GetDest().IsGroup())
{
packetType = PACKET_MULTICAST;
}
else
{
packetType = PACKET_OTHERHOST;
}
if (!m_promiscCallback.IsNull())
{
Simulator::Schedule(hardwareLatency,
&TsnNetDevice::m_macPromiscRxTrace,
this,
originalPacket->Copy());
Simulator::Schedule(hardwareLatency,
&TsnNetDevice::m_promiscCallback,
this,
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_rxCallback(this, packet, protocol, header.GetSrc());
if(!m_rxCallbackWithTimestamp.IsNull())
{
m_rxCallbackWithTimestamp(this, packet, protocol, header.GetSrc(), rxTimestamp);
}
m_latencyTrace(originalPacket);
}
}
}
Ptr<TsnNode>
TsnNetDevice::GetNode()
{
return m_node;
}
void
TsnNetDevice::SetReceiveCallbackWithTimestamp(ReceiveCallbackWithTimestamp cb)
{
m_rxCallbackWithTimestamp = cb;
}
void
TsnNetDevice::SetTransmitCallbackWithTimestamp(TransmitCallbackWithTimestamp cb)
{
m_txCallbackWithTimestamp = cb;
}
void
TsnNetDevice::AddGclEntry(Time duration, uint8_t states)
{
NS_LOG_FUNCTION(this);
m_tas->AddGclEntry(duration, states);
}
void
TsnNetDevice::StartTas()
{
NS_LOG_FUNCTION(this);
StartTas(Time(0));
}
void
TsnNetDevice::StartTas(Time startTime)
{
NS_LOG_FUNCTION(this);
Simulator::Schedule(startTime, &Tas::Start, m_tas);
}
Ptr<Tas>
TsnNetDevice::GetTas()
{
NS_LOG_FUNCTION(this);
return m_tas;
}
}

View File

@@ -0,0 +1,199 @@
#ifndef TSN_NET_DEVICE_H
#define TSN_NET_DEVICE_H
#include "ns3/ethernet-net-device.h"
#include "ns3/packet.h"
#include "ns3/queue.h"
#include "ns3/tsn-node.h"
#include "ns3/tsn-transmission-selection-algo.h"
#include "ns3/tas.h"
// Add a doxygen group for this module.
// If you have more than one file, this should be in only one of them.
/**
* \defgroup tsn Description of the tsn
*/
namespace ns3
{
class Tas;
class TsnNode;
class TsnNetDevice : public EthernetNetDevice
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a TsnNetDevice
*/
TsnNetDevice();
/**
* Destroy a TsnNetDevice
*
* This is the destructor for the TsnNetDevice.
*/
~TsnNetDevice();
/**
* Attach a queue to the TsnNetDevice.
*
* The TsnNetDevice "owns" a queue that implements a queueing
* method such as DropTailQueue
*
* \param queue Ptr to the new queue.
*/
void SetQueue(Ptr<Queue<Packet>> queue);
/**
* Attach a queue and a Transmission Selection Algo to the TsnNetDevice.
*
* \param queue Ptr to the new queue.
* \param tsa Ptr to the new Transmission Selection Algo.
*/
void SetQueue(Ptr<Queue<Packet>> q, Ptr<TsnTransmissionSelectionAlgo> tsa);
/**
* Update existing queue with a queue and a Transmission Selection Algo.
*
* \param queue_id of the queue to update
* \param queue Ptr to the new queue.
* \param tsa Ptr to the new Transmission Selection Algo.
*/
void UpdateQueue(uint32_t queue_id, Ptr<Queue<Packet>> q, Ptr<TsnTransmissionSelectionAlgo> tsa);
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype) override;
/**
* This function is use to put GPTP frame in a specific FIFO without using QTAG
*/
bool SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype,
uint8_t pcp);
/**
* This function is use to put GPTP frame in a specific FIFO without using QTAG
*/
bool SendWithSpecificFIFO(Ptr<Packet> packet, const Address& dest, uint16_t ethertype, uint8_t pcp);
/**
* Receive a packet from a connected TsnNetDevice.
*
* The TsnNetDevice 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.
*/
void Receive(Ptr<Packet> p) override;
typedef Callback<bool, Ptr<TsnNetDevice>, Ptr<const Packet>, uint16_t, const Address&, Time> ReceiveCallbackWithTimestamp;
void SetReceiveCallbackWithTimestamp(ReceiveCallbackWithTimestamp cb);
typedef Callback<bool, Ptr<const Packet>, Time> TransmitCallbackWithTimestamp;
void SetTransmitCallbackWithTimestamp(TransmitCallbackWithTimestamp cb);
void SetNode(Ptr<TsnNode> node);
void AddGclEntry(Time duration, uint8_t states);
void StartTas();
void StartTas(Time startTime);
Ptr<Tas> GetTas();
void CheckForReadyPacket() override;
Ptr<TsnNode> GetNode();
protected :
/**
* 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
* \param queue_id queue id of the packet to send
* \returns true if success, false on failure
*/
bool TransmitStart(Ptr<Packet> p, int queue_id);
/**
* 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(int queue_id);
/**
*
*
*/
int TransmitSelection() override;
/**
* The trace source fired when packets are drop by FRER recovery function
*/
TracedCallback<Ptr<const Packet>> m_frerDropTrace;
/**
* The trace source fired when packets are drop by the MaxSDUSizeFilter (PSFP)
*/
TracedCallback<Ptr<const Packet>> m_maxSDUSizeFilterDropTrace;
/**
* The trace source fired when packets are drop by the Flow Meter (PSFP)
*/
TracedCallback<Ptr<const Packet>> m_REDFrameDropTrace;
ReceiveCallbackWithTimestamp m_rxCallbackWithTimestamp;
TransmitCallbackWithTimestamp m_txCallbackWithTimestamp;
//FRER
/**
* The TransmissionSelectionAlgos which this TsnNetDevice uses
*/
std::vector<Ptr<TsnTransmissionSelectionAlgo>> m_transmissionSelectionAlgos;
Ptr<TsnNode> m_node; //!< TSN Node owning this NetDevice
bool m_txHardwareLatencyExperienced = false;
private:
/**
* Find the nextEligible Queue for transimission
*
* \returns Int id to the queue.
*/
Ptr<Tas> m_tas;
EventId m_NextGatesUpdate;
};
}
#endif /* TSN_NET_DEVICE_H */

View File

@@ -0,0 +1,285 @@
#include "tsn-node.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/names.h"
#include "ns3/clock.h"
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-aggregated-net-device.h"
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/switch-net-device.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TsnNode");
NS_OBJECT_ENSURE_REGISTERED(TsnNode);
TypeId
TsnNode::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnNode")
.SetParent<Node>()
.SetGroupName("tsn")
.AddConstructor<TsnNode>()
.AddAttribute("MaxSidEntryNumber",
"The maximum number of entry in the stream identification function table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxSidEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxPsfpFilterEntryNumber",
"The maximum number of entry in the stream filter table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxPsfpFilterEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxPsfpStreamGateEntryNumber",
"The maximum number of entry in the stream gate table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxPsfpStreamGateEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxPsfpFlowMeterEntryNumber",
"The maximum number of entry in the flow meter table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxPsfpFlowMeterEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxFrerSeqGenEntryNumber",
"The maximum number of entry in the sequence generation table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxFrerSeqGenEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxFrerSeqRcvyEntryNumber",
"The maximum number of entry in the sequence recovery table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxFrerSeqRcvyEntryNumber),
MakeUintegerChecker<uint16_t>())
.AddAttribute("MaxFrerSeqEncEntryNumber",
"The maximum number of entry in the sequence encode table",
UintegerValue(65535),
MakeUintegerAccessor(&TsnNode::m_maxFrerSeqEncEntryNumber),
MakeUintegerChecker<uint16_t>());
return tid;
}
TsnNode::TsnNode() : Node()
{
NS_LOG_FUNCTION(this);
m_mainClock = CreateObject<Clock>();
}
TsnNode::TsnNode(uint32_t sid) : Node(sid)
{
NS_LOG_FUNCTION(this << sid);
}
TsnNode::~TsnNode()
{
NS_LOG_FUNCTION(this);
}
uint32_t
TsnNode::AddDevice(Ptr<TsnNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
device->SetNode(this);
return Node::AddDevice(device);
}
uint32_t
TsnNode::AddDevice(Ptr<TsnAggregatedNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
device->SetNode(this);
return Node::AddDevice(device);
}
uint32_t
TsnNode::AddDevice(Ptr<TsnMultidropNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
device->SetNode(this);
return Node::AddDevice(device);
}
uint32_t
TsnNode::AddDevice(Ptr<SwitchNetDevice> device)
{
NS_LOG_FUNCTION(this << device);
device->SetNode(this);
return Node::AddDevice(device);
}
void
TsnNode::SetMainClock(Ptr<Clock> clock)
{
NS_LOG_FUNCTION(this);
clock->AddClockCorrectionCallback(MakeCallback(&TsnNode::CallBackClockUpdate, this));
m_mainClock = clock;
}
void
TsnNode::AddClock(Ptr<Clock> clock)
{
NS_LOG_FUNCTION(this);
clock->AddClockCorrectionCallback(MakeCallback(&TsnNode::CallBackClockUpdate, this));
m_clocks.insert(m_clocks.end(), clock);
}
void
TsnNode::CallBackClockUpdate()
{
NS_LOG_FUNCTION(this);
//Update all TAS on each node net device
for(int i = 0; i<(int)GetNDevices(); i++)
{
if( GetDevice(i)->GetObject<TsnNetDevice>() != nullptr)
{
GetDevice(i)->GetObject<TsnNetDevice>()->GetTas()->ClockUpdate();
}
}
}
void
TsnNode::setActiveClock(uint32_t activeClockId)
{
NS_LOG_FUNCTION(this);
m_activeClockId = activeClockId;
}
Ptr<Clock>
TsnNode::GetActiveClock()
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_clocks.size() > m_activeClockId, "No active clock on Node " << this);
return m_clocks[m_activeClockId];
}
Time
TsnNode::GetTime() const
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_mainClock != nullptr, "No main clock was declared with SetMainClock(Ptr<Clock> clock) on Node " << this);
return m_mainClock->GetLocalTime();
}
Time
TsnNode::GetTime(uint32_t clockId) const
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_clocks.size() > clockId, "No clock with clockId "<< clockId << " on Node " << this);
return m_clocks[clockId]->GetLocalTime();
}
//Stream identification
void
TsnNode::AddStreamIdentificationFunction(
uint32_t streamHandle,
Ptr<StreamIdentificationFunction> streamIdentificationFunction,
std::vector<Ptr<TsnNetDevice>> outFacInputPortList,
std::vector<Ptr<TsnNetDevice>> inFacInputPortList,
std::vector<Ptr<TsnNetDevice>> inFacOutputPortList,
std::vector<Ptr<TsnNetDevice>> outFacOutputPortList)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_streamIdentityTable.size() < m_maxSidEntryNumber, "Trying to add more stream identification function than the " << m_maxSidEntryNumber << " available.");
Ptr<StreamIdEntry> entry = CreateObject<StreamIdEntry>();
entry->SetAttribute("StreamHandle", UintegerValue(streamHandle));
entry->SetStreamIdentificationFunction(streamIdentificationFunction);
entry->SetOutFacInputPortList(outFacInputPortList);
entry->SetInFacInputPortList(inFacInputPortList);
entry->SetInFacOutputPortList(inFacOutputPortList);
entry->SetOutFacOutputPortList(outFacOutputPortList);
m_streamIdentityTable.insert(m_streamIdentityTable.end(),entry);
}
std::vector<Ptr<StreamIdEntry>>
TsnNode::GetStreamIdentityTable(){
NS_LOG_FUNCTION(this);
return m_streamIdentityTable;
}
//PSFP
void
TsnNode::AddStreamFilter(Ptr<StreamFilterInstance> streamFilterInstance)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_streamFilterInstanceTable.size() < m_maxPsfpFilterEntryNumber, "Trying to add more stream filter than the " << m_maxPsfpFilterEntryNumber << " available.");
m_streamFilterInstanceTable.insert(m_streamFilterInstanceTable.end(),streamFilterInstance);
}
uint16_t
TsnNode::AddFlowMeter(Ptr<FlowMeterInstance> flowMeterInstance)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_flowMeterInstanceTable.size() < m_maxPsfpFlowMeterEntryNumber, "Trying to add more flow meter than the " << m_maxPsfpFlowMeterEntryNumber << " available.");
m_flowMeterInstanceTable.insert(m_flowMeterInstanceTable.end(),flowMeterInstance);
return m_flowMeterInstanceTable.size() - 1;
}
std::vector<Ptr<StreamFilterInstance>>
TsnNode::GetStreamFilterInstanceTable()
{
NS_LOG_FUNCTION(this);
return m_streamFilterInstanceTable;
}
std::vector<Ptr<FlowMeterInstance>>
TsnNode::GetFlowMeterInstanceTable()
{
NS_LOG_FUNCTION(this);
return m_flowMeterInstanceTable;
}
//FRER
void
TsnNode::AddSequenceGenerationFunction(Ptr<SequenceGenerationFunction> entry)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqGenTable.size() < m_maxFrerSeqGenEntryNumber, "Trying to add more sequence generation fonction than the " << m_maxFrerSeqGenEntryNumber << " available.");
m_frerSeqGenTable.insert(m_frerSeqGenTable.end(),entry);
}
std::vector<Ptr<SequenceGenerationFunction>>
TsnNode::GetSequenceGenerationTable()
{
NS_LOG_FUNCTION(this);
return m_frerSeqGenTable;
}
void
TsnNode::AddSequenceRecoveryFunction(Ptr<SequenceRecoveryFunction> entry)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqRcvyTable.size() < m_maxFrerSeqRcvyEntryNumber, "Trying to add more sequence recovery fonction than the " << m_maxFrerSeqRcvyEntryNumber << " available.");
m_frerSeqRcvyTable.insert(m_frerSeqRcvyTable.end(),entry);
}
std::vector<Ptr<SequenceRecoveryFunction>>
TsnNode::GetSequenceRecoveryTable()
{
NS_LOG_FUNCTION(this);
return m_frerSeqRcvyTable;
}
void
TsnNode::AddSequenceEncodeDecodeFunction(Ptr<SequenceEncodeDecodeFunction> entry)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_frerSeqEncTable.size() < m_maxFrerSeqEncEntryNumber, "Trying to add more sequence encode/decode fonction than the " << m_maxFrerSeqEncEntryNumber << " available.");
m_frerSeqEncTable.insert(m_frerSeqEncTable.end(),entry);
}
std::vector<Ptr<SequenceEncodeDecodeFunction>>
TsnNode::GetSequenceEncodeDecodeTable()
{
NS_LOG_FUNCTION(this);
return m_frerSeqEncTable;
}
}

View File

@@ -0,0 +1,150 @@
#ifndef TSN_NODE_H
#define TSN_NODE_H
#include "ns3/node.h"
#include "ns3/traced-callback.h"
#include "ns3/clock.h"
#include "ns3/tsn-net-device.h"
#include "ns3/switch-net-device.h"
#include "stream-identity-entry.h"
#include "stream-identification-function.h"
#include "frer-sequence-generation-function.h"
#include "frer-sequence-encode-decode-function.h"
#include "frer-sequence-recovery-function.h"
#include "psfp-stream-filter-instance.h"
#include "psfp-flow-meter-instance.h"
namespace ns3
{
class TsnNetDevice;
class TsnAggregatedNetDevice;
class TsnMultidropNetDevice;
class SwitchNetDevice;
class StreamIdEntry;
class SequenceGenerationFunction;
class SequenceEncodeDecodeFunction;
class SequenceRecoveryFunction;
/**
* \ingroup tsn
*
* \brief A network tsn Node.
*
* It add clocks to the ns3 vanilla Node
*/
class TsnNode: public Node
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a TsnNode
*/
TsnNode();
/**
* \param systemId a unique integer used for parallel simulations.
*/
TsnNode(uint32_t systemId);
uint32_t AddDevice(Ptr<TsnNetDevice> device);
uint32_t AddDevice(Ptr<TsnAggregatedNetDevice> device);
uint32_t AddDevice(Ptr<TsnMultidropNetDevice> device);
uint32_t AddDevice(Ptr<SwitchNetDevice> device);
Ptr<Clock> GetActiveClock();
Time GetTime() const;
Time GetTime(uint32_t clockId) const;
/**
* Destroy a TsnNode
*
* This is the destructor for the TsnNode.
*/
~TsnNode();
// Delete copy constructor and assignment operator to avoid misuse
TsnNode& operator=(const TsnNode&) = delete;
TsnNode(const TsnNode&) = delete;
void SetMainClock(Ptr<Clock> clock);
void AddClock(Ptr<Clock> clock);
void setActiveClock(uint32_t activeClockId);
//Stream identification
void AddStreamIdentificationFunction(
uint32_t streamHandle,
Ptr<StreamIdentificationFunction> streamIdentificationFunction,
std::vector<Ptr<TsnNetDevice>> outFacInputPortList,
std::vector<Ptr<TsnNetDevice>> inFacInputPortList,
std::vector<Ptr<TsnNetDevice>> inFacOutputPortList,
std::vector<Ptr<TsnNetDevice>> outFacOutputPortList);
std::vector<Ptr<StreamIdEntry>> GetStreamIdentityTable();
//FRER
void AddSequenceGenerationFunction(Ptr<SequenceGenerationFunction> entry);
std::vector<Ptr<SequenceGenerationFunction>> GetSequenceGenerationTable();
void AddSequenceRecoveryFunction(Ptr<SequenceRecoveryFunction> entry);
std::vector<Ptr<SequenceRecoveryFunction>> GetSequenceRecoveryTable();
void AddSequenceEncodeDecodeFunction(Ptr<SequenceEncodeDecodeFunction> entry);
std::vector<Ptr<SequenceEncodeDecodeFunction>> GetSequenceEncodeDecodeTable();
//PSFP
void AddStreamFilter(Ptr<StreamFilterInstance> streamFilterInstance);
uint16_t AddFlowMeter(Ptr<FlowMeterInstance> flowMeterInstance);
std::vector<Ptr<StreamFilterInstance>> GetStreamFilterInstanceTable();
std::vector<Ptr<FlowMeterInstance>> GetFlowMeterInstanceTable();
void CallBackClockUpdate();
protected:
private:
Ptr<Clock> m_mainClock = nullptr;
uint32_t m_activeClockId = 0;
/**
* The Clocks which this TsnNode uses
*/
std::vector<Ptr<Clock>> m_clocks;
//StreamIdenty
uint16_t m_maxSidEntryNumber;
std::vector<Ptr<StreamIdEntry>> m_streamIdentityTable;
//FRER
uint16_t m_maxFrerSeqGenEntryNumber;
uint16_t m_maxFrerSeqRcvyEntryNumber;
uint16_t m_maxFrerSeqEncEntryNumber;
std::vector<Ptr<SequenceGenerationFunction>> m_frerSeqGenTable;
std::vector<Ptr<SequenceRecoveryFunction>> m_frerSeqRcvyTable;
std::vector<Ptr<SequenceEncodeDecodeFunction>> m_frerSeqEncTable;
// std::vector<Ptr<frerSplitEntry>> m_frerSplitTable;
//PSFP
uint16_t m_maxPsfpFilterEntryNumber;
uint16_t m_maxPsfpStreamGateEntryNumber;
uint16_t m_maxPsfpFlowMeterEntryNumber;
std::vector<Ptr<StreamFilterInstance>> m_streamFilterInstanceTable;
// std::vector<Ptr<StreamGateInstance>> m_streamGateInstanceTable;
std::vector<Ptr<FlowMeterInstance>> m_flowMeterInstanceTable;
};
}
#endif /* TSN_NODE_H */

View File

@@ -0,0 +1,102 @@
#include "tsn-transmission-selection-algo.h"
#include "ns3/log.h"
#include "ns3/nstime.h"
#include "ns3/packet.h"
#include "ns3/random-variable-stream.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("TsnTransmissionSelectionAlgo");
NS_OBJECT_ENSURE_REGISTERED(TsnTransmissionSelectionAlgo);
TypeId
TsnTransmissionSelectionAlgo::GetTypeId()
{
static TypeId tid =
TypeId("ns3::TsnTransmissionSelectionAlgo")
.SetParent<Object>()
.SetGroupName("Tsn")
.AddConstructor<TsnTransmissionSelectionAlgo>()
.AddAttribute("MultidropMode",
"Mode for the 10Base-T1S",
BooleanValue(false),
MakeBooleanAccessor(&TsnTransmissionSelectionAlgo::m_MultidropMode),
MakeBooleanChecker())
.AddAttribute("MinLatencyOverhead",
"The minimum latency overhead cause by the CBS hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&TsnTransmissionSelectionAlgo::m_minLatencyOverhead),
MakeTimeChecker())
.AddAttribute("MaxLatencyOverhead",
"The maximun latency overhead cause by the CBS hardware implementation",
TimeValue(Seconds(0)),
MakeTimeAccessor(&TsnTransmissionSelectionAlgo::m_maxLatencyOverhead),
MakeTimeChecker());
return tid;
}
TsnTransmissionSelectionAlgo::TsnTransmissionSelectionAlgo()
{
NS_LOG_FUNCTION(this);
}
TsnTransmissionSelectionAlgo::~TsnTransmissionSelectionAlgo()
{
NS_LOG_FUNCTION(this);
}
void
TsnTransmissionSelectionAlgo::SetTsnNetDevice(Ptr<TsnNetDevice> net)
{
m_net = net;
ReadyToTransmitCallback = MakeCallback(&TsnNetDevice::CheckForReadyPacket, m_net);
}
void
TsnTransmissionSelectionAlgo::SetQueue(Ptr<Queue<Packet>> queue)
{
NS_LOG_FUNCTION(this);
m_queue = queue;
}
bool
TsnTransmissionSelectionAlgo::IsReadyToTransmit()
{
NS_LOG_FUNCTION(this);
return true;
}
void
TsnTransmissionSelectionAlgo::TransmitStart(Ptr<Packet> p, Time txTime)
{
NS_LOG_FUNCTION(this);
}
void
TsnTransmissionSelectionAlgo::TransmitComplete(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this);
}
void
TsnTransmissionSelectionAlgo::UpdateTransmissionGate(bool IsOpen)
{
NS_LOG_FUNCTION(this);
m_transmissionGateIsOpen = IsOpen;
}
Time
TsnTransmissionSelectionAlgo::GetHardwareLatency()
{
NS_LOG_FUNCTION(this);
Ptr<UniformRandomVariable> randVar = CreateObject<UniformRandomVariable>();
return NanoSeconds(randVar->GetValue(m_minLatencyOverhead.GetNanoSeconds(), m_maxLatencyOverhead.GetNanoSeconds()));
}
};

View File

@@ -0,0 +1,71 @@
#ifndef TSN_TRANSMISSION_SELECTION_ALGO_H
#define TSN_TRANSMISSION_SELECTION_ALGO_H
#include "ns3/object.h"
#include "ns3/packet.h"
#include "ns3/nstime.h"
#include "ns3/tsn-net-device.h"
namespace ns3
{
class TsnNetDevice;
class TsnTransmissionSelectionAlgo: public Object
{
public:
/**
* \brief Get the TypeId
*
* \return The TypeId for this class
*/
static TypeId GetTypeId();
/**
* \brief Create a TsnTransmissionSelectionAlgo
*/
TsnTransmissionSelectionAlgo();
/**
* Destroy a TsnTransmissionSelectionAlgo
*
* This is the destructor for the TsnTransmissionSelectionAlgo.
*/
~TsnTransmissionSelectionAlgo();
// Delete copy constructor and assignment operator to avoid misuse
TsnTransmissionSelectionAlgo& operator=(const TsnTransmissionSelectionAlgo&) = delete;
TsnTransmissionSelectionAlgo(const TsnTransmissionSelectionAlgo&) = delete;
void SetTsnNetDevice(Ptr<TsnNetDevice> net);
void SetQueue(Ptr<Queue<Packet>> queue);
virtual bool IsReadyToTransmit();
virtual void TransmitStart(Ptr<Packet> p, Time txTime);
virtual void TransmitComplete(Ptr<Packet> p);
virtual void UpdateTransmissionGate(bool IsOpen);
Time GetHardwareLatency();
protected:
Ptr<TsnNetDevice> m_net;
Ptr<Queue<Packet>> m_queue;
bool m_transmissionGateIsOpen;
bool m_MultidropMode;
Callback<void> ReadyToTransmitCallback;
private:
Time m_minLatencyOverhead;
Time m_maxLatencyOverhead;
};
}
#endif /* TSN_TRANSMISSION_SELECTION_ALGO_H */

View File

@@ -0,0 +1,283 @@
// Include a header file from your module to test.
#include "ns3/tsn-net-device.h"
#include "ns3/cbs.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-generator.h"
#include "ns3/switch-net-device.h"
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/core-module.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("CbsTestSuite");
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup cbs-tests Tests for cbs
* \ingroup tsn
* \ingroup tests
*/
/**
* \ingroup Cbs-tests
* Check if message crossed a point to point tsn channel
*/
class CbsBasicTestCase : public TestCase
{
public:
CbsBasicTestCase();
virtual ~CbsBasicTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
CbsBasicTestCase::CbsBasicTestCase()
: TestCase("Check if paquets cross a point to point ethernet channel, tsn net device and a CBS")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
CbsBasicTestCase::~CbsBasicTestCase()
{
}
void
CbsBasicTestCase::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
CbsBasicTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
CbsBasicTestCase::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create CBS and add FIFOs
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("5Mb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(DataRate("1Gb/s")));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&CbsBasicTestCase::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&CbsBasicTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_sent, m_received, "All Packets sent have been received");
}
/**
* \ingroup Cbs-tests
* Check last message latency with a point to point network
*/
class CbsLatencyTestCase : public TestCase
{
public:
CbsLatencyTestCase(uint16_t burstSize, Time latency, DataRate datarate);
virtual ~CbsLatencyTestCase();
private:
void DoRun() override;
void Latency(Ptr<const Packet> p);
uint16_t m_burstSize;
Time m_true_latency;
Time m_latency;
DataRate m_datarate;
};
CbsLatencyTestCase::CbsLatencyTestCase(uint16_t burstSize, Time latency, DataRate datarate)
: TestCase("Check last message latency with a point to point network with CBS")
{
m_burstSize = burstSize;
m_true_latency = latency;
m_datarate = datarate;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
CbsLatencyTestCase::~CbsLatencyTestCase()
{
}
void
CbsLatencyTestCase::Latency(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
CbsLatencyTestCase::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
net0->SetAttribute("DataRate", DataRateValue(m_datarate));
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
net1->SetAttribute("DataRate", DataRateValue(m_datarate));
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create CBS and add FIFOs
Ptr<Cbs> cbs = CreateObject<Cbs>();
cbs->SetTsnNetDevice(net0);
cbs->SetAttribute("IdleSlope", DataRateValue(DataRate("5Mb/s")));
cbs->SetAttribute("portTransmitRate", DataRateValue(m_datarate));
net0->SetQueue(CreateObject<DropTailQueue<Packet>>(), cbs);
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burstSize));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(1));
//Callback to trace the latency
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&CbsLatencyTestCase::Latency, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency, m_true_latency, "Packet experience the correct latency");
}
// The TestSuite class names the TestSuite, identifies what type of TestSuite,
// and enables the TestCases to be run. Typically, only the constructor for
// this class must be defined
/**
* \ingroup cbs-tests
* TestSuite for module cbs
*/
class CbsTestSuite : public TestSuite
{
public:
CbsTestSuite();
};
CbsTestSuite::CbsTestSuite()
: TestSuite("cbs", UNIT)
{
LogComponentEnable("CbsTestSuite", LOG_LEVEL_ALL);
LogComponentEnable("Cbs", LOG_LEVEL_ALL);
//Point to point network with cbs
AddTestCase(new CbsBasicTestCase, TestCase::QUICK);
AddTestCase(new CbsLatencyTestCase(1, Time(NanoSeconds((1400 + 22 + 8)*8 +25)), DataRate("1Gb/s")), TestCase::QUICK);
AddTestCase(new CbsLatencyTestCase(2, Time(NanoSeconds(2307200 + (1400 + 22 + 8)*8 + 25)), DataRate("1Gb/s")), TestCase::QUICK); //2307200 is the time needed to restore the credit after an emission
AddTestCase(new CbsLatencyTestCase(3, Time(NanoSeconds(2307200*2 + (1400 + 22 + 8)*8 + 25)), DataRate("1Gb/s")), TestCase::QUICK);
AddTestCase(new CbsLatencyTestCase(10, Time(NanoSeconds(2307200*9 + (1400 + 22 + 8)*8 + 25)), DataRate("1Gb/s")), TestCase::QUICK);
if(Time::GetResolution()==Time::PS){
AddTestCase(new CbsLatencyTestCase(2, Time(NanoSeconds(2307200 + (1400 + 22 + 8)*3.2 + 25)), DataRate("2.5Gb/s")), TestCase::QUICK);
AddTestCase(new CbsLatencyTestCase(2, Time(NanoSeconds(2307200 + (1400 + 22 + 8)*1.6 + 25)), DataRate("5Gb/s")), TestCase::QUICK);
AddTestCase(new CbsLatencyTestCase(2, Time(NanoSeconds(2307200 + (1400 + 22 + 8)*0.8 + 25)), DataRate("10Gb/s")), TestCase::QUICK);
}
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup cbs-tests
* Static variable for test initialization
*/
static CbsTestSuite m_cbsTestSuite;

View File

@@ -0,0 +1,43 @@
#! /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", "True", "True"),
("tsn-point2point-withCBS", "True", "True"),
("tsn-point2point-withTAS", "True", "True"),
("tsn-point2point-withTAS-CBS", "True", "True"),
("tsn-point2point-withTAS-GuardBand --none", "True", "True"),
("tsn-point2point-withTAS-GuardBand --mtu", "True", "True"),
("tsn-point2point-withTAS-GuardBand --pktsize", "True", "True"),
("tsn-point2point-withTAS-gPTP", "True", "True"),
("tsn-point2point-withPSFP-MaxSDUSizeFilter", "True", "True"),
("tsn-point2point-withPSFP-FlowMeter", "True", "True"),
("tsn-switched-withFRER", "True", "True"),
("tsn-switched-withFRER-recoveryAlgo --match", "True", "True"),
("tsn-switched-withFRER-recoveryAlgo --vector", "True", "True"),
("tsn-switched-withFRER-activeSid", "True", "True"),
("tsn-switched-withFRERonES-integratedSW", "True", "True"),
("tsn-switched-withFRERonES-aggregation", "True", "True"),
("tsn-point2point-withGPTP", "True", "True"),
("tsn-point2point-withGPTP-Multidomain", "True", "True"),
("tsn-point2point-withGPTP-fixPrecisionClock", "True", "True"),
("tsn-switched-withGPTP", "True", "True"),
("tsn-multidrop", "True", "True"),
("tsn-multidrop-withCBS", "True", "True"),
("tsn-multidrop-withTAS", "True", "True"),
("tsn-multidrop-withTAS-CBS", "True", "True"),
("tsn-switched-multidrop", "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,359 @@
// Include a header file from your module to test.
#include "ns3/tsn-node.h"
#include "ns3/tsn-net-device.h"
#include "ns3/switch-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-generator.h"
#include "ns3/ethernet-header2.h"
#include "ns3/stream-identification-function.h"
#include "ns3/stream-identification-function-null.h"
#include "ns3/frer-sequence-generation-function.h"
#include "ns3/frer-sequence-recovery-function.h"
#include "ns3/frer-latent-error-detection-function.h"
#include "ns3/frer-match-recovery-function.h"
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/core-module.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("FRERTestSuite");
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup FRER-tests Tests for FRER
* \ingroup tsn
* \ingroup tests
*/
/**
* \ingroup FRER-tests
* Check if paquets are correctly replicated and eliminated on a 4 SW / 2 ES network
*
* / ==== SW2 ==== \
* ESsource ==== SW1 SW4 ==== ESdest
* \ ==== SW3 ==== /
*
* The FRER use in this test use the SW1 to do the replication
* using mulitcast and SW4 to do the elimination. The stream identification is
* done by the null stream identification function. The match recovery function,
* latent error detection function are also used in this example.
* Two flows go from ESsource to ESdest. Only the VLAN 100 flow use FRER mechanisms.
*
*
*/
class FRERBasicMulticastTestCase : public TestCase
{
public:
FRERBasicMulticastTestCase();
virtual ~FRERBasicMulticastTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_replicated{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
uint64_t m_pktSize = 500;
uint64_t m_expected_replicated = (1400 + 22 + 6) * 2 + (500+22); //the 1400Bytes pkt is replicated two time and RTAGed (+6bits), the 500Bytes pkt is not replicated
uint64_t m_expected_receive = (1400 + 22) + (500+22);
};
// Add some help text to this case to describe what it is intended to test
FRERBasicMulticastTestCase::FRERBasicMulticastTestCase()
: TestCase("Check if paquets are correctly replicated and eliminated")
{}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
FRERBasicMulticastTestCase::~FRERBasicMulticastTestCase()
{
}
void
FRERBasicMulticastTestCase::SendTx(Ptr<const Packet> p)
{
m_replicated += p->GetSize();
}
void
FRERBasicMulticastTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
FRERBasicMulticastTestCase::DoRun()
{
//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"), 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(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(net0);
app1->SetAttribute("BurstSize", UintegerValue(1));
app1->SetAttribute("PayloadSize", UintegerValue(500));
app1->SetAttribute("Period", TimeValue(MilliSeconds(20)));
app1->SetAttribute("PCP", UintegerValue(0));
app1->SetAttribute("VlanID", UintegerValue(101));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(0));
app1->SetStopTime(MilliSeconds(20));
//Callback to trace the message being replicated and eliminated
net2_2->TraceConnectWithoutContext("MacTx",
MakeCallback(&FRERBasicMulticastTestCase::SendTx, this));
net2_3->TraceConnectWithoutContext("MacTx",
MakeCallback(&FRERBasicMulticastTestCase::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&FRERBasicMulticastTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(MilliSeconds(50));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_replicated, m_expected_replicated, "All Packets have been replicated");
NS_TEST_ASSERT_MSG_EQ(m_received, m_expected_receive, "All Packets have been eliminated");
}
// The TestSuite class names the TestSuite, identifies what type of TestSuite,
// and enables the TestCases to be run. Typically, only the constructor for
// this class must be defined
/**
* \ingroup FRER-tests
* TestSuite for module FRER
*/
class FRERTestSuite : public TestSuite
{
public:
FRERTestSuite();
};
FRERTestSuite::FRERTestSuite()
: TestSuite("frer", UNIT)
{
LogComponentEnable("FRERTestSuite", LOG_LEVEL_ALL);
//Pkt in a switched network with multicast FRER
AddTestCase(new FRERBasicMulticastTestCase(), TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup FRER-tests
* Static variable for test initialization
*/
static FRERTestSuite m_FRERTestSuite;

View File

@@ -0,0 +1,279 @@
// Include a header file from your module to test.
#include "ns3/tsn-net-device.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-generator.h"
#include "ns3/switch-net-device.h"
#include "ns3/stream-identification-function-null.h"
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/core-module.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("PsfpTestSuite");
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup Psfp-tests Tests for Psfp
* \ingroup tsn
* \ingroup tests
*/
/**
* \ingroup Psfp-tests
* Check if message crossed a point to point tsn channel
*/
class PsfpBasicTestCase : public TestCase
{
public:
PsfpBasicTestCase(
uint64_t receivedExpected,
uint16_t streamHandle,
uint8_t priority,
uint16_t maxSDUSize,
bool streamBlockedDueToOversizeFrameEnable,
DataRate cir,
uint16_t cbs,
DataRate eir,
uint16_t ebs,
bool dropOnYellow,
bool cf,
bool markAllFramesRedEnable);
virtual ~PsfpBasicTestCase();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
uint64_t m_received_expected;
uint16_t m_streamHandle;
uint8_t m_priority;
uint16_t m_maxSDUSize;
bool m_streamBlockedDueToOversizeFrameEnable;
DataRate m_cir;
uint16_t m_cbs;
DataRate m_eir;
uint16_t m_ebs;
bool m_dropOnYellow;
bool m_cf;
bool m_markAllFramesRedEnable;
};
// Add some help text to this case to describe what it is intended to test
PsfpBasicTestCase::PsfpBasicTestCase(
uint64_t receivedExpected,
uint16_t streamHandle,
uint8_t priority,
uint16_t maxSDUSize,
bool streamBlockedDueToOversizeFrameEnable,
DataRate cir,
uint16_t cbs,
DataRate eir,
uint16_t ebs,
bool dropOnYellow,
bool cf,
bool markAllFramesRedEnable)
: TestCase("Check if paquets cross a point to point ethernet channel, tsn net device and PSFP")
{
m_received_expected = receivedExpected;
m_streamHandle = streamHandle;
m_priority = priority;
m_maxSDUSize = maxSDUSize;
m_streamBlockedDueToOversizeFrameEnable = streamBlockedDueToOversizeFrameEnable;
m_cir = cir;
m_cbs = cbs;
m_eir = eir;
m_ebs = ebs;
m_dropOnYellow = dropOnYellow;
m_cf = cf;
m_markAllFramesRedEnable = markAllFramesRedEnable;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
PsfpBasicTestCase::~PsfpBasicTestCase()
{
}
void
PsfpBasicTestCase::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
PsfpBasicTestCase::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
PsfpBasicTestCase::DoRun()
{
//Create two nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> net1 = CreateObject<TsnNetDevice>();
n1->AddDevice(net1);
//Create a Tsn Channel and attach it two the two netDevices
Ptr<EthernetChannel> channel = CreateObject<EthernetChannel>();
net0->Attach(channel);
net1->Attach(channel);
//Allocate a Mac address for each netDevice.
net0->SetAddress(Mac48Address::Allocate());
net1->SetAddress(Mac48Address::Allocate());
//Create and add FIFOs
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net0->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
net1->SetQueue(CreateObject<DropTailQueue<Packet>>());
//Stream identification
Ptr<NullStreamIdentificationFunction> sif0 = CreateObject<NullStreamIdentificationFunction>();
uint16_t StreamHandle = 10;
sif0->SetAttribute("VlanID", UintegerValue(1));
sif0->SetAttribute("Address", AddressValue(Mac48Address("ff:ff:ff:ff:ff:ff")));
n1->AddStreamIdentificationFunction(StreamHandle, sif0, {net1}, {}, {}, {});
//PSFP configuration - Stream Filter
Ptr<StreamFilterInstance> sfi0 = CreateObject<StreamFilterInstance>();
sfi0->SetAttribute("StreamHandle", IntegerValue(m_streamHandle));
sfi0->SetAttribute("Priority", IntegerValue(m_priority));
sfi0->SetAttribute("MaxSDUSize", UintegerValue(m_maxSDUSize));
sfi0->SetAttribute("StreamBlockedDueToOversizeFrameEnable", BooleanValue(m_streamBlockedDueToOversizeFrameEnable));
n1->AddStreamFilter(sfi0);
//PSFP configuration - Flow meter
Ptr<FlowMeterInstance> fm0 = CreateObject<FlowMeterInstance>();
fm0->SetAttribute("CIR", DataRateValue(m_cir));
fm0->SetAttribute("CBS", UintegerValue(m_cbs));
fm0->SetAttribute("EIR", DataRateValue(m_eir));
fm0->SetAttribute("EBS", UintegerValue(m_ebs));
fm0->SetAttribute("DropOnYellow", BooleanValue(m_dropOnYellow));
fm0->SetAttribute("CF", BooleanValue(m_cf));
fm0->SetAttribute("MarkAllFramesRedEnable", BooleanValue(m_markAllFramesRedEnable));
uint16_t fmid = n1->AddFlowMeter(fm0);
sfi0->AddFlowMeterInstanceId(fmid);
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(5));
app0->SetAttribute("PayloadSize", UintegerValue(1000));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app0);
app0->SetStartTime(Seconds(1));
app0->SetStopTime(Seconds(11));
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(5));
app1->SetAttribute("PayloadSize", UintegerValue(1400));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(1));
app1->SetAttribute("PCP", UintegerValue(0));
n0->AddApplication(app1);
app1->SetStartTime(Seconds(1.1));
app1->SetStopTime(Seconds(11.1));
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&PsfpBasicTestCase::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&PsfpBasicTestCase::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(14));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * (5 * (1400 + 22) + 5 * (1000 + 22)), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_received, m_received_expected, "All Packets sent have been received");
}
// 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 psfp-tests
* TestSuite for module psfp
*/
class PsfpTestSuite : public TestSuite
{
public:
PsfpTestSuite();
};
PsfpTestSuite::PsfpTestSuite()
: TestSuite("psfp", UNIT)
{
LogComponentEnable("PsfpTestSuite", LOG_LEVEL_ALL);
//Point to point network with psfp
// Stream filter test
// All packet should pass because PSFP is well configured
AddTestCase(new PsfpBasicTestCase(2 * (5 * (1400 + 22) + 5 *(1000 + 22)), 10, 0, 1422, false, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// No packet should pass due to maxSDUSize filter
AddTestCase(new PsfpBasicTestCase(0, 10, 0, 1021, false, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// All packet should pass because the priority spec is wrong
AddTestCase(new PsfpBasicTestCase(2 * (5 * (1400 + 22) + 5 *(1000 + 22)), 10, 1, 1021, false, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// All packet should pass because the stream handle spec is wrong
AddTestCase(new PsfpBasicTestCase(2 * (5 * (1400 + 22) + 5 *(1000 + 22)), 11, 0, 1021, false, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// Only 2*5 packets of 1022octet packet should pass due to maxSDUSize filter
AddTestCase(new PsfpBasicTestCase(2 * (5 *(1000 + 22)),10 ,0 , 1022, false, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// Only 5 packets of 1022octet packet should pass due to maxSDUSize filter with m_streamBlockedDueToOversizeFrameEnable=true
AddTestCase(new PsfpBasicTestCase((5 *(1000 + 22)),10 ,0 , 1022, true, DataRate("1Gb/s"), 65535, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// Flow meter test
//Two packets of 1022octet should pass in green
AddTestCase(new PsfpBasicTestCase(2 * (1000 + 22), 10 ,0 , 1422, false, DataRate("8000b/s"), 1022, DataRate("0Gb/s"), 0, false, false, false), TestCase::QUICK);
//One packets of 1022octet should pass in green
AddTestCase(new PsfpBasicTestCase(1 * (1000 + 22), 10 ,0 , 1422, false, DataRate("7999b/s"), 1022, DataRate("0Gb/s"), 0, false, false, false), TestCase::QUICK);
//Six packets of 1022octet should pass in green
AddTestCase(new PsfpBasicTestCase(6 * (1000 + 22), 10 ,0 , 1422, false, DataRate("8000b/s"), 65535, DataRate("0Gb/s"), 0, false, false, false), TestCase::QUICK);
//One packets of 1022octet should pass in green and the rest are drop due to MarkAllFramesRedEnable
AddTestCase(new PsfpBasicTestCase(1000 + 22, 10 ,0 , 1422, false, DataRate("8000b/s"), 65535, DataRate("0Gb/s"), 0, false, false, true), TestCase::QUICK);
// All packet should pass in yellow
AddTestCase(new PsfpBasicTestCase(2 * (5 * (1400 + 22) + 5 *(1000 + 22)),10 ,0 , 1422, false, DataRate("0Gb/s"), 0, DataRate("1Gb/s"), 65535, false, false, false), TestCase::QUICK);
// All packet should pass in yellow due to compling flag
AddTestCase(new PsfpBasicTestCase(2 * (5 * (1400 + 22) + 5 *(1000 + 22)),10 ,0 , 1422, false, DataRate("1Gb/s"), 999, DataRate("0Gb/s"), 65535, false, true, false), TestCase::QUICK);
// All packet should not pass due to dropOnYellow=true
AddTestCase(new PsfpBasicTestCase(0, 10 ,0 , 1422, false, DataRate("0Gb/s"), 0, DataRate("1Gb/s"), 65535, true, false, false), TestCase::QUICK);
// All packet should pass in yellow due to compling flag and dropOnYellow=true
AddTestCase(new PsfpBasicTestCase(0,10 ,0 , 1422, false, DataRate("1Gb/s"), 999, DataRate("0Gb/s"), 65535, true, true, false), TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup psfp-tests
* Static variable for test initialization
*/
static PsfpTestSuite m_psfpTestSuite;

View File

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

View File

@@ -0,0 +1,863 @@
// Include a header file from your module to test.
#include "ns3/tsn-multidrop-net-device.h"
#include "ns3/tsn-multidrop-channel.h"
#include "ns3/tsn-net-device.h"
#include "ns3/tsn-node.h"
#include "ns3/ethernet-channel.h"
#include "ns3/ethernet-header2.h"
#include "ns3/ethernet-generator.h"
#include "ns3/switch-net-device.h"
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/core-module.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/timestamp-tag.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("TsnMultidropTestSuite");
// Add a doxygen group for tests.
// If you have more than one test, this should be in only one of them.
/**
* \defgroup tsn-multidrop-tests Tests for multidrop tsn
* \ingroup tsn
* \ingroup tests
*/
/**
* \ingroup tsn-multidrop-tests
* Check if message crossed an tsn multidrop channel
*/
class TsnMultiDropBasicTestCase1Flow : public TestCase
{
public:
TsnMultiDropBasicTestCase1Flow();
virtual ~TsnMultiDropBasicTestCase1Flow();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
TsnMultiDropBasicTestCase1Flow::TsnMultiDropBasicTestCase1Flow()
: TestCase("Check if message crossed an tsn multidrop channel")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnMultiDropBasicTestCase1Flow::~TsnMultiDropBasicTestCase1Flow()
{
}
void
TsnMultiDropBasicTestCase1Flow::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
TsnMultiDropBasicTestCase1Flow::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnMultiDropBasicTestCase1Flow::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
n0->AddDevice(net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(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(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
n0->AddApplication(app0);
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&TsnMultiDropBasicTestCase1Flow::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnMultiDropBasicTestCase1Flow::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times");
NS_TEST_ASSERT_MSG_EQ(m_sent, m_received, "All Packets sent have been received");
}
/**
* \ingroup tsn-multidrop-tests
* Check if message crossed an tsn multidrop channel from multiple
* sources.
*/
class TsnMultiDropBasicTestCase3Flows : public TestCase
{
public:
TsnMultiDropBasicTestCase3Flows();
virtual ~TsnMultiDropBasicTestCase3Flows();
private:
void DoRun() override;
void SendTx(Ptr<const Packet> p);
void ReceiveRx(Ptr<const Packet> p);
uint64_t m_sent{0}; //!< number of bytes sent
uint64_t m_received{0}; //!< number of bytes received
};
// Add some help text to this case to describe what it is intended to test
TsnMultiDropBasicTestCase3Flows::TsnMultiDropBasicTestCase3Flows()
: TestCase("Check if message crossed an tsn multidrop channel from"
" multiple sources")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnMultiDropBasicTestCase3Flows::~TsnMultiDropBasicTestCase3Flows()
{
}
void
TsnMultiDropBasicTestCase3Flows::SendTx(Ptr<const Packet> p)
{
m_sent += p->GetSize();
}
void
TsnMultiDropBasicTestCase3Flows::ReceiveRx(Ptr<const Packet> p)
{
m_received += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnMultiDropBasicTestCase3Flows::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
n0->AddDevice(net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(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>>());
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>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(10));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetStartTime(Seconds(0));
app0->SetStopTime(Seconds(10));
n0->AddApplication(app0);
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(1));
app1->SetAttribute("PCP", UintegerValue(1));
app1->SetStartTime(Seconds(0));
app1->SetStopTime(Seconds(10));
n1->AddApplication(app1);
Ptr<EthernetGenerator> app2 = CreateObject<EthernetGenerator>();
app2->Setup(net2);
app2->SetAttribute("BurstSize", UintegerValue(2));
app2->SetAttribute("PayloadSize", UintegerValue(500));
app2->SetAttribute("Period", TimeValue(Seconds(5)));
app2->SetAttribute("VlanID", UintegerValue(1));
app2->SetAttribute("PCP", UintegerValue(1));
app2->SetStartTime(Seconds(6));
app2->SetStopTime(Seconds(7));
n3->AddApplication(app2);
//Callback to trace the message being send and received
net0->TraceConnectWithoutContext("MacTx",
MakeCallback(&TsnMultiDropBasicTestCase3Flows::SendTx, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnMultiDropBasicTestCase3Flows::ReceiveRx, this));
net2->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnMultiDropBasicTestCase3Flows::ReceiveRx, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_sent, 2 * 10 * (1400 + 22), "10 Packets have been sent two times by net0");
NS_TEST_ASSERT_MSG_EQ(m_received, 2*2*10*(1400 + 22) + 2*1*(100+22) + 2*1*(500+22), "All Packets sent have been received");
}
/**
* \ingroup tsn-multidrop-tests
* Check if message crossed an tsn multidrop channel with the expected latency
*/
class TsnMultiDropLatencyTestCase1Flow : public TestCase
{
public:
TsnMultiDropLatencyTestCase1Flow(bool isLocalNodeId0, Time startTime, Time expectedLatency);
virtual ~TsnMultiDropLatencyTestCase1Flow();
private:
void DoRun() override;
void LatencyCallback(Ptr<const Packet> p);
bool m_is_local_node_id_0;
Time m_expected_latency;
Time m_latency;
Time m_start_time;
};
// Add some help text to this case to describe what it is intended to test
TsnMultiDropLatencyTestCase1Flow::TsnMultiDropLatencyTestCase1Flow(bool isLocalNodeId0, Time startTime, Time expectedLatency)
: TestCase("Check if message crossed an tsn multidrop channel with the expected latency")
{
m_is_local_node_id_0 = isLocalNodeId0;
m_expected_latency = expectedLatency;
m_start_time = startTime;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnMultiDropLatencyTestCase1Flow::~TsnMultiDropLatencyTestCase1Flow()
{
}
void
TsnMultiDropLatencyTestCase1Flow::LatencyCallback(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
if (ethHeader.GetVid()!=1)
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnMultiDropLatencyTestCase1Flow::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
if (m_is_local_node_id_0){
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
}
else
{
net0->SetAttribute("PLCALocalNodeId", UintegerValue(2));
}
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
n0->AddDevice(net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
if (m_is_local_node_id_0){
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
}
else
{
net2->SetAttribute("PLCALocalNodeId", UintegerValue(0));
}
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(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(1));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetStartTime(m_start_time);
app0->SetStopTime(Seconds(1));
n0->AddApplication(app0);
//Callback to trace the message being send and received
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnMultiDropLatencyTestCase1Flow::LatencyCallback, this));
//Execute the simulation
Simulator::Stop(Seconds(1));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency, m_expected_latency, "The simulated latency is equal to the expected latency");
}
/**
* \ingroup tsn-multidrop-tests
* Check if message crossed an tsn multidrop channel with the expected latency
* when the channel is shared with two other flows
*/
class TsnMultiDropLatencyTestCase3Flows : public TestCase
{
public:
TsnMultiDropLatencyTestCase3Flows(uint8_t burstSize, uint8_t priority, Time startTime, Time expectedLatency);
virtual ~TsnMultiDropLatencyTestCase3Flows();
private:
void DoRun() override;
void LatencyCallback(Ptr<const Packet> p);
uint8_t m_burst_size;
uint8_t m_priority;
Time m_expected_latency;
Time m_latency;
Time m_start_time;
};
// Add some help text to this case to describe what it is intended to test
TsnMultiDropLatencyTestCase3Flows::TsnMultiDropLatencyTestCase3Flows(uint8_t burstSize, uint8_t priority, Time startTime, Time expectedLatency)
: TestCase("Check if message crossed an tsn multidrop channel with the "
"expected latency when the channel is shared with two other flows")
{
m_burst_size = burstSize;
m_priority = priority;
m_expected_latency = expectedLatency;
m_start_time = startTime;
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnMultiDropLatencyTestCase3Flows::~TsnMultiDropLatencyTestCase3Flows()
{
}
void
TsnMultiDropLatencyTestCase3Flows::LatencyCallback(Ptr<const Packet> p)
{
TimestampTag tag;
if (!p->FindFirstMatchingByteTag(tag))
{
return;
}
Ptr<Packet> originalPacket = p->Copy();
EthernetHeader2 ethHeader;
originalPacket->RemoveHeader(ethHeader);
if (ethHeader.GetVid()!=1)
{
return;
}
Time arrival = Simulator::Now();
m_latency = arrival - tag.GetTimestamp();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnMultiDropLatencyTestCase3Flows::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnMultidropNetDevice> net0 = CreateObject<TsnMultidropNetDevice>();
net0->SetAttribute("PLCALocalNodeId", UintegerValue(0));
net0->SetAttribute("PLCANodeCount", UintegerValue(4));
n0->AddDevice(net0);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(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>>());
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>>());
//Application description
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("BurstSize", UintegerValue(m_burst_size));
app0->SetAttribute("PayloadSize", UintegerValue(1400));
app0->SetAttribute("Period", TimeValue(Seconds(5)));
app0->SetAttribute("VlanID", UintegerValue(1));
app0->SetAttribute("PCP", UintegerValue(m_priority));
app0->SetStartTime(m_start_time);
app0->SetStopTime(Seconds(1));
n0->AddApplication(app0);
Ptr<EthernetGenerator> app1 = CreateObject<EthernetGenerator>();
app1->Setup(net0);
app1->SetAttribute("BurstSize", UintegerValue(2));
app1->SetAttribute("PayloadSize", UintegerValue(1000));
app1->SetAttribute("Period", TimeValue(Seconds(5)));
app1->SetAttribute("VlanID", UintegerValue(2));
app1->SetAttribute("PCP", UintegerValue(0));
app1->SetStartTime(NanoSeconds(10));
app1->SetStopTime(Seconds(1));
n0->AddApplication(app1);
Ptr<EthernetGenerator> app2 = CreateObject<EthernetGenerator>();
app2->Setup(net2);
app2->SetAttribute("BurstSize", UintegerValue(1));
app2->SetAttribute("PayloadSize", UintegerValue(100));
app2->SetAttribute("Period", TimeValue(Seconds(5)));
app2->SetAttribute("VlanID", UintegerValue(3));
app2->SetAttribute("PCP", UintegerValue(1));
app2->SetStartTime(NanoSeconds(0));
app2->SetStopTime(Seconds(1));
n2->AddApplication(app2);
//Callback to trace the message being send and received
net1->TraceConnectWithoutContext("Latency",
MakeCallback(&TsnMultiDropLatencyTestCase3Flows::LatencyCallback, this));
//Execute the simulation
Simulator::Stop(Seconds(1));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_latency, m_expected_latency, "The simulated latency is equal to the expected latency");
}
/**
* \ingroup tsn-multidrop-tests
* Check if message can crossed from a switched network to a 10Base-T1S bus
* and from a 10Base-T1S bus to a switched network
*/
class TsnMultiDropSwitchedBasicTestCase : public TestCase
{
public:
TsnMultiDropSwitchedBasicTestCase();
virtual ~TsnMultiDropSwitchedBasicTestCase();
private:
void DoRun() override;
void ReceiveRxFrom10BaseT1SBus(Ptr<const Packet> p);
void ReceiveRxFromSwitchedNetwork(Ptr<const Packet> p);
uint64_t m_received_from_switched_network{0}; //!< number of bytes
uint64_t m_received_from_10BaseT1S_bus{0}; //!< number of bytes
};
// Add some help text to this case to describe what it is intended to test
TsnMultiDropSwitchedBasicTestCase::TsnMultiDropSwitchedBasicTestCase()
: TestCase("Check if message can crossed from a switched network to a 10Base-T1S"
"bus and from a 10Base-T1S bus to a switched network")
{
}
// This destructor does nothing but we include it as a reminder that
// the test case should clean up after itself
TsnMultiDropSwitchedBasicTestCase::~TsnMultiDropSwitchedBasicTestCase()
{
}
void
TsnMultiDropSwitchedBasicTestCase::ReceiveRxFrom10BaseT1SBus(Ptr<const Packet> p)
{
m_received_from_10BaseT1S_bus += p->GetSize();
}
void
TsnMultiDropSwitchedBasicTestCase::ReceiveRxFromSwitchedNetwork(Ptr<const Packet> p)
{
m_received_from_switched_network += p->GetSize();
}
//
// This method is the pure virtual method from class TestCase that every
// TestCase must implement
//
void
TsnMultiDropSwitchedBasicTestCase::DoRun()
{
//Create four nodes
Ptr<TsnNode> n0 = CreateObject<TsnNode>();
Ptr<TsnNode> n1 = CreateObject<TsnNode>();
Ptr<TsnNode> n2 = CreateObject<TsnNode>();
Ptr<TsnNode> n3 = CreateObject<TsnNode>();
Ptr<TsnNode> n4 = CreateObject<TsnNode>();
//Create and add a netDevice to each node
Ptr<TsnNetDevice> net0 = CreateObject<TsnNetDevice>();
n0->AddDevice(net0);
Ptr<TsnNetDevice> swnet0 = CreateObject<TsnNetDevice>();
n4->AddDevice(swnet0);
Ptr<TsnMultidropNetDevice> swnet1 = CreateObject<TsnMultidropNetDevice>();
swnet1->SetAttribute("PLCALocalNodeId", UintegerValue(0));
swnet1->SetAttribute("PLCANodeCount", UintegerValue(4));
n4->AddDevice(swnet1);
Ptr<TsnMultidropNetDevice> net1 = CreateObject<TsnMultidropNetDevice>();
net1->SetAttribute("PLCALocalNodeId", UintegerValue(1));
net1->SetAttribute("PLCANodeCount", UintegerValue(4));
n1->AddDevice(net1);
Ptr<TsnMultidropNetDevice> net2 = CreateObject<TsnMultidropNetDevice>();
net2->SetAttribute("PLCALocalNodeId", UintegerValue(2));
net2->SetAttribute("PLCANodeCount", UintegerValue(4));
n2->AddDevice(net2);
Ptr<TsnMultidropNetDevice> net3 = CreateObject<TsnMultidropNetDevice>();
net3->SetAttribute("PLCALocalNodeId", UintegerValue(3));
net3->SetAttribute("PLCANodeCount", UintegerValue(4));
n3->AddDevice(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(net1->GetAddress()), 10, {swnet1});
sw->AddForwardingTableEntry(Mac48Address::ConvertFrom(net0->GetAddress()), 20, {swnet0});
//Application description
//From switched network to 10Base-T1S
Ptr<EthernetGenerator> app0 = CreateObject<EthernetGenerator>();
app0->Setup(net0);
app0->SetAttribute("Address", AddressValue(net1->GetAddress()));
app0->SetAttribute("BurstSize", UintegerValue(5));
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("Address", AddressValue(net0->GetAddress()));
app1->SetAttribute("BurstSize", UintegerValue(2));
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 trace the message being send and received
net0->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnMultiDropSwitchedBasicTestCase::ReceiveRxFrom10BaseT1SBus, this));
net1->TraceConnectWithoutContext("MacRx",
MakeCallback(&TsnMultiDropSwitchedBasicTestCase::ReceiveRxFromSwitchedNetwork, this));
//Execute the simulation
Simulator::Stop(Seconds(12));
Simulator::Run();
Simulator::Destroy();
NS_TEST_ASSERT_MSG_EQ(m_received_from_10BaseT1S_bus, 2 * 2 * (100 + 22), "4 Packets have been received two times by net0");
NS_TEST_ASSERT_MSG_EQ(m_received_from_switched_network, 2 * 5 * (1400 + 22), "10 Packets have been received two times by net1");
}
// The TestSuite class names the TestSuite, identifies what type of TestSuite,
// and enables the TestCases to be run. Typically, only the constructor for
// this class must be defined
/**
* \ingroup tsn-multidrop-tests
* TestSuite for module tsn with a focus on multidrop
*/
class TsnMultidropTestSuite : public TestSuite
{
public:
TsnMultidropTestSuite();
};
TsnMultidropTestSuite::TsnMultidropTestSuite()
: TestSuite("tsn-multidrop", UNIT)
{
LogComponentEnable("TsnMultidropTestSuite", LOG_LEVEL_ALL);
//Multidrop channel
AddTestCase(new TsnMultiDropBasicTestCase1Flow, TestCase::QUICK);
AddTestCase(new TsnMultiDropBasicTestCase3Flows, TestCase::QUICK);
// Latency with one flow on the channel
//Wait for 10Base-T1S init when producer is local nodeId = 0
AddTestCase(new TsnMultiDropLatencyTestCase1Flow(true, Time(NanoSeconds(0)), Time(NanoSeconds(((1400 + 22 + 8) + 1 * 20 + 4 * 32)*800 + 25))), TestCase::QUICK);
//Frame ready just before the opportunity when producer is local nodeId = 0
AddTestCase(new TsnMultiDropLatencyTestCase1Flow(true, Time(NanoSeconds((20 + 4 *32) * 800 - 1)), Time(NanoSeconds((1400 + 22 + 8)*800 + 1 + 25))), TestCase::QUICK);
//Wait for 10Base-T1S init when producer is local nodeId = 2
AddTestCase(new TsnMultiDropLatencyTestCase1Flow(false, Time(NanoSeconds(0)), Time(NanoSeconds(((1400 + 22 + 8) + 1 * 20 + (4 + 2) * 32)*800 + 25))), TestCase::QUICK);
//Latency with multiple flows on the channel
AddTestCase(new TsnMultiDropLatencyTestCase3Flows(1, 0, Time(NanoSeconds(0)), Time(NanoSeconds(((1400 + 22 + 8) + 1 * 20 + 4 * 32)*800 + 25))), TestCase::QUICK);
AddTestCase(new TsnMultiDropLatencyTestCase3Flows(2, 0, Time(NanoSeconds(0)), Time(NanoSeconds(((1400 + 22 + 8) + 2 * 20 + (4+2) * 32 + (1400 + 22 + 8 + 12 + 100 + 22 + 8 + 12))*800 + 25 * 3))), TestCase::QUICK);
AddTestCase(new TsnMultiDropLatencyTestCase3Flows(2, 0, Time(NanoSeconds(11)), Time(NanoSeconds(((1400 + 22 + 8) + 4 * 20 + (4+ 2 + 3 + 3) * 32 + (1042 * 2 + 1442 + 142))*800 + 25 * 3 - 11))), TestCase::QUICK);
AddTestCase(new TsnMultiDropLatencyTestCase3Flows(2, 1, Time(NanoSeconds(11)), Time(NanoSeconds(((1400 + 22 + 8) + 2 * 20 + (4+ 2) * 32 + (1442 + 142))*800 + 25 * 3 - 11))), TestCase::QUICK);
//Switched network connected to a multidrop channel
AddTestCase(new TsnMultiDropSwitchedBasicTestCase, TestCase::QUICK);
}
// Do not forget to allocate an instance of this TestSuite
/**
* \ingroup tsn-tests
* Static variable for test initialization
*/
static TsnMultidropTestSuite m_tsnMultidropTestSuite;

File diff suppressed because it is too large Load Diff