329 lines
10 KiB
C++
329 lines
10 KiB
C++
#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()));
|
|
}
|
|
|
|
};
|