#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() .SetGroupName("Tsn") .AddConstructor() .AddAttribute("PLCALocalNodeId", "Local node ID for PLCA", UintegerValue(0), MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCALocalNodeId), MakeUintegerChecker()) .AddAttribute("PLCANodeCount", "Number of node in the collision domain", UintegerValue(8), MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCANodeCount), MakeUintegerChecker()) .AddAttribute("PLCATransmitOpportunityTimer", "Max number of bit time between transmit opportunity", UintegerValue(32), MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCATransmitOpportunityTimer), MakeUintegerChecker()) .AddAttribute("PLCAMaxBurstCount", "Max number of additional packet that can be transmit in a single opportunity", UintegerValue(0), MakeUintegerAccessor(&TsnMultidropNetDevice::m_PLCAMaxBurstCount), MakeUintegerChecker()) .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()) .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 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 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 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 #"<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 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) { 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 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> 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> 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 fmids = streamFilterInstanceTable[i]->GetFlowMeterIds(); std::vector> 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 #"<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> 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> 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, const Address& source, const Address& dest, uint16_t ethertype) { NS_LOG_FUNCTION(this); // Stream identification in-facing output std::vector> 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 #"<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> 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> seqRcvyTable = GetNode()->GetSequenceRecoveryTable(); for (uint32_t i = 0; i < seqRcvyTable.size(); i++) { bool IsApplicable(uint32_t streamHandle, Ptr 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 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 #"<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 #"<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; } }