Update README and add contrib dir
This commit is contained in:
84
contrib/tsn/CMakeLists.txt
Normal file
84
contrib/tsn/CMakeLists.txt
Normal 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}
|
||||
)
|
||||
36
contrib/tsn/examples/CMakeLists.txt
Normal file
36
contrib/tsn/examples/CMakeLists.txt
Normal 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()
|
||||
191
contrib/tsn/examples/tsn-multidrop-withCBS.cc
Normal file
191
contrib/tsn/examples/tsn-multidrop-withCBS.cc
Normal 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;
|
||||
}
|
||||
292
contrib/tsn/examples/tsn-multidrop-withTAS-CBS.cc
Normal file
292
contrib/tsn/examples/tsn-multidrop-withTAS-CBS.cc
Normal 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;
|
||||
}
|
||||
274
contrib/tsn/examples/tsn-multidrop-withTAS.cc
Normal file
274
contrib/tsn/examples/tsn-multidrop-withTAS.cc
Normal 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;
|
||||
}
|
||||
254
contrib/tsn/examples/tsn-multidrop.cc
Normal file
254
contrib/tsn/examples/tsn-multidrop.cc
Normal 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;
|
||||
}
|
||||
119
contrib/tsn/examples/tsn-point2point-withCBS.cc
Normal file
119
contrib/tsn/examples/tsn-point2point-withCBS.cc
Normal 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;
|
||||
}
|
||||
127
contrib/tsn/examples/tsn-point2point-withGPTP-Multidomain.cc
Normal file
127
contrib/tsn/examples/tsn-point2point-withGPTP-Multidomain.cc
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
121
contrib/tsn/examples/tsn-point2point-withGPTP.cc
Normal file
121
contrib/tsn/examples/tsn-point2point-withGPTP.cc
Normal 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;
|
||||
}
|
||||
142
contrib/tsn/examples/tsn-point2point-withPSFP-FlowMeter.cc
Normal file
142
contrib/tsn/examples/tsn-point2point-withPSFP-FlowMeter.cc
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
168
contrib/tsn/examples/tsn-point2point-withTAS-CBS.cc
Normal file
168
contrib/tsn/examples/tsn-point2point-withTAS-CBS.cc
Normal 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;
|
||||
}
|
||||
199
contrib/tsn/examples/tsn-point2point-withTAS-GuardBand.cc
Normal file
199
contrib/tsn/examples/tsn-point2point-withTAS-GuardBand.cc
Normal 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;
|
||||
}
|
||||
191
contrib/tsn/examples/tsn-point2point-withTAS-gPTP.cc
Normal file
191
contrib/tsn/examples/tsn-point2point-withTAS-gPTP.cc
Normal 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;
|
||||
}
|
||||
148
contrib/tsn/examples/tsn-point2point-withTAS.cc
Normal file
148
contrib/tsn/examples/tsn-point2point-withTAS.cc
Normal 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;
|
||||
}
|
||||
92
contrib/tsn/examples/tsn-point2point.cc
Normal file
92
contrib/tsn/examples/tsn-point2point.cc
Normal 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;
|
||||
}
|
||||
301
contrib/tsn/examples/tsn-switched-multidrop.cc
Normal file
301
contrib/tsn/examples/tsn-switched-multidrop.cc
Normal 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;
|
||||
}
|
||||
358
contrib/tsn/examples/tsn-switched-withFRER-activeSid.cc
Normal file
358
contrib/tsn/examples/tsn-switched-withFRER-activeSid.cc
Normal 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;
|
||||
}
|
||||
362
contrib/tsn/examples/tsn-switched-withFRER-recoveryAlgo.cc
Normal file
362
contrib/tsn/examples/tsn-switched-withFRER-recoveryAlgo.cc
Normal 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;
|
||||
}
|
||||
320
contrib/tsn/examples/tsn-switched-withFRER.cc
Normal file
320
contrib/tsn/examples/tsn-switched-withFRER.cc
Normal 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;
|
||||
}
|
||||
285
contrib/tsn/examples/tsn-switched-withFRERonES-aggregation.cc
Normal file
285
contrib/tsn/examples/tsn-switched-withFRERonES-aggregation.cc
Normal 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;
|
||||
}
|
||||
324
contrib/tsn/examples/tsn-switched-withFRERonES-integratedSW.cc
Normal file
324
contrib/tsn/examples/tsn-switched-withFRERonES-integratedSW.cc
Normal 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;
|
||||
}
|
||||
209
contrib/tsn/examples/tsn-switched-withGPTP.cc
Normal file
209
contrib/tsn/examples/tsn-switched-withGPTP.cc
Normal 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;
|
||||
}
|
||||
8
contrib/tsn/helper/tsn-helper.cc
Normal file
8
contrib/tsn/helper/tsn-helper.cc
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "tsn-helper.h"
|
||||
|
||||
namespace ns3
|
||||
{
|
||||
|
||||
/* ... */
|
||||
|
||||
}
|
||||
16
contrib/tsn/helper/tsn-helper.h
Normal file
16
contrib/tsn/helper/tsn-helper.h
Normal 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
177
contrib/tsn/model/cbs.cc
Normal 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
74
contrib/tsn/model/cbs.h
Normal 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 */
|
||||
106
contrib/tsn/model/clock-constant-drift.cc
Normal file
106
contrib/tsn/model/clock-constant-drift.cc
Normal 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()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
56
contrib/tsn/model/clock-constant-drift.h
Normal file
56
contrib/tsn/model/clock-constant-drift.h
Normal 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 */
|
||||
83
contrib/tsn/model/clock-fix-precision.cc
Normal file
83
contrib/tsn/model/clock-fix-precision.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
64
contrib/tsn/model/clock-fix-precision.h
Normal file
64
contrib/tsn/model/clock-fix-precision.h
Normal 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 */
|
||||
77
contrib/tsn/model/clock-virtual.cc
Normal file
77
contrib/tsn/model/clock-virtual.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
52
contrib/tsn/model/clock-virtual.h
Normal file
52
contrib/tsn/model/clock-virtual.h
Normal 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 */
|
||||
98
contrib/tsn/model/clock.cc
Normal file
98
contrib/tsn/model/clock.cc
Normal 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
70
contrib/tsn/model/clock.h
Normal 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 */
|
||||
82
contrib/tsn/model/frer-base-recovery-function.cc
Normal file
82
contrib/tsn/model/frer-base-recovery-function.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
68
contrib/tsn/model/frer-base-recovery-function.h
Normal file
68
contrib/tsn/model/frer-base-recovery-function.h
Normal 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 */
|
||||
127
contrib/tsn/model/frer-latent-error-detection-function.cc
Normal file
127
contrib/tsn/model/frer-latent-error-detection-function.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
71
contrib/tsn/model/frer-latent-error-detection-function.h
Normal file
71
contrib/tsn/model/frer-latent-error-detection-function.h
Normal 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 */
|
||||
78
contrib/tsn/model/frer-match-recovery-function.cc
Normal file
78
contrib/tsn/model/frer-match-recovery-function.cc
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
contrib/tsn/model/frer-match-recovery-function.h
Normal file
47
contrib/tsn/model/frer-match-recovery-function.h
Normal 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 */
|
||||
104
contrib/tsn/model/frer-sequence-encode-decode-function.cc
Normal file
104
contrib/tsn/model/frer-sequence-encode-decode-function.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
56
contrib/tsn/model/frer-sequence-encode-decode-function.h
Normal file
56
contrib/tsn/model/frer-sequence-encode-decode-function.h
Normal 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 */
|
||||
70
contrib/tsn/model/frer-sequence-generation-function.cc
Normal file
70
contrib/tsn/model/frer-sequence-generation-function.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
54
contrib/tsn/model/frer-sequence-generation-function.h
Normal file
54
contrib/tsn/model/frer-sequence-generation-function.h
Normal 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 */
|
||||
129
contrib/tsn/model/frer-sequence-recovery-function.cc
Normal file
129
contrib/tsn/model/frer-sequence-recovery-function.cc
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
67
contrib/tsn/model/frer-sequence-recovery-function.h
Normal file
67
contrib/tsn/model/frer-sequence-recovery-function.h
Normal 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 */
|
||||
140
contrib/tsn/model/frer-vector-recovery-function.cc
Normal file
140
contrib/tsn/model/frer-vector-recovery-function.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
51
contrib/tsn/model/frer-vector-recovery-function.h
Normal file
51
contrib/tsn/model/frer-vector-recovery-function.h
Normal 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 */
|
||||
282
contrib/tsn/model/gPTP-header.cc
Normal file
282
contrib/tsn/model/gPTP-header.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
104
contrib/tsn/model/gPTP-header.h
Normal file
104
contrib/tsn/model/gPTP-header.h
Normal 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 */
|
||||
232
contrib/tsn/model/gPTP-packet.cc
Normal file
232
contrib/tsn/model/gPTP-packet.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
121
contrib/tsn/model/gPTP-packet.h
Normal file
121
contrib/tsn/model/gPTP-packet.h
Normal 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
771
contrib/tsn/model/gPTP.cc
Normal 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
219
contrib/tsn/model/gPTP.h
Normal 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 */
|
||||
156
contrib/tsn/model/psfp-flow-meter-instance.cc
Normal file
156
contrib/tsn/model/psfp-flow-meter-instance.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
61
contrib/tsn/model/psfp-flow-meter-instance.h
Normal file
61
contrib/tsn/model/psfp-flow-meter-instance.h
Normal 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 */
|
||||
119
contrib/tsn/model/psfp-stream-filter-instance.cc
Normal file
119
contrib/tsn/model/psfp-stream-filter-instance.cc
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
68
contrib/tsn/model/psfp-stream-filter-instance.h
Normal file
68
contrib/tsn/model/psfp-stream-filter-instance.h
Normal 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 */
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
76
contrib/tsn/model/stream-identification-function-null.cc
Normal file
76
contrib/tsn/model/stream-identification-function-null.cc
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
52
contrib/tsn/model/stream-identification-function-null.h
Normal file
52
contrib/tsn/model/stream-identification-function-null.h
Normal 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 */
|
||||
67
contrib/tsn/model/stream-identification-function.cc
Normal file
67
contrib/tsn/model/stream-identification-function.cc
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
53
contrib/tsn/model/stream-identification-function.h
Normal file
53
contrib/tsn/model/stream-identification-function.h
Normal 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 */
|
||||
131
contrib/tsn/model/stream-identity-entry.cc
Normal file
131
contrib/tsn/model/stream-identity-entry.cc
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
67
contrib/tsn/model/stream-identity-entry.h
Normal file
67
contrib/tsn/model/stream-identity-entry.h
Normal 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
328
contrib/tsn/model/tas.cc
Normal 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
118
contrib/tsn/model/tas.h
Normal 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 */
|
||||
65
contrib/tsn/model/transmission-gate.cc
Normal file
65
contrib/tsn/model/transmission-gate.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
63
contrib/tsn/model/transmission-gate.h
Normal file
63
contrib/tsn/model/transmission-gate.h
Normal 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 */
|
||||
241
contrib/tsn/model/tsn-aggregated-net-device.cc
Normal file
241
contrib/tsn/model/tsn-aggregated-net-device.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
70
contrib/tsn/model/tsn-aggregated-net-device.h
Normal file
70
contrib/tsn/model/tsn-aggregated-net-device.h
Normal 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 */
|
||||
154
contrib/tsn/model/tsn-multidrop-channel.cc
Normal file
154
contrib/tsn/model/tsn-multidrop-channel.cc
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
174
contrib/tsn/model/tsn-multidrop-channel.h
Normal file
174
contrib/tsn/model/tsn-multidrop-channel.h
Normal 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 */
|
||||
758
contrib/tsn/model/tsn-multidrop-net-device.cc
Normal file
758
contrib/tsn/model/tsn-multidrop-net-device.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
228
contrib/tsn/model/tsn-multidrop-net-device.h
Normal file
228
contrib/tsn/model/tsn-multidrop-net-device.h
Normal 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 */
|
||||
694
contrib/tsn/model/tsn-net-device.cc
Normal file
694
contrib/tsn/model/tsn-net-device.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
199
contrib/tsn/model/tsn-net-device.h
Normal file
199
contrib/tsn/model/tsn-net-device.h
Normal 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 */
|
||||
285
contrib/tsn/model/tsn-node.cc
Normal file
285
contrib/tsn/model/tsn-node.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
150
contrib/tsn/model/tsn-node.h
Normal file
150
contrib/tsn/model/tsn-node.h
Normal 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 */
|
||||
102
contrib/tsn/model/tsn-transmission-selection-algo.cc
Normal file
102
contrib/tsn/model/tsn-transmission-selection-algo.cc
Normal 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()));
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
71
contrib/tsn/model/tsn-transmission-selection-algo.h
Normal file
71
contrib/tsn/model/tsn-transmission-selection-algo.h
Normal 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 */
|
||||
283
contrib/tsn/test/cbs-test-suite.cc
Normal file
283
contrib/tsn/test/cbs-test-suite.cc
Normal 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;
|
||||
43
contrib/tsn/test/examples-to-run.py
Normal file
43
contrib/tsn/test/examples-to-run.py
Normal 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 = []
|
||||
359
contrib/tsn/test/frer-test-suite.cc
Normal file
359
contrib/tsn/test/frer-test-suite.cc
Normal 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;
|
||||
279
contrib/tsn/test/psfp-test-suite.cc
Normal file
279
contrib/tsn/test/psfp-test-suite.cc
Normal 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;
|
||||
715
contrib/tsn/test/tas-test-suite.cc
Normal file
715
contrib/tsn/test/tas-test-suite.cc
Normal 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;
|
||||
863
contrib/tsn/test/tsn-multidrop-test-suite.cc
Normal file
863
contrib/tsn/test/tsn-multidrop-test-suite.cc
Normal 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;
|
||||
1012
contrib/tsn/test/tsn-test-suite.cc
Normal file
1012
contrib/tsn/test/tsn-test-suite.cc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user