#include "tas.h" #include "ns3/log.h" #include "ns3/simulator.h" #include "ns3/data-rate.h" #include "ns3/enum.h" #include #include #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() .SetGroupName("Tsn") .AddConstructor() .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()) .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 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 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()); } 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 activeClock = m_net->GetNode()->GetObject()->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()->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 p, DataRate d, uint8_t preambleAndSFDGap, uint8_t interframeGap, uint32_t mtu) { NS_LOG_FUNCTION(this); Ptr clock = m_net->GetNode()->GetObject()->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 randVar = CreateObject(); return NanoSeconds(randVar->GetValue(m_minLatencyOverhead.GetNanoSeconds(), m_maxLatencyOverhead.GetNanoSeconds())); } };