EMANE  1.0.1
basemodelimpl.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2016 - Adjacent Link LLC, Bridgewater, New Jersey
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of Adjacent Link LLC nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "basemodelimpl.h"
35 
40 #include "emane/mactypes.h"
41 
48 
49 #include "txslotinfosformatter.h"
50 #include "basemodelmessage.h"
51 #include "priority.h"
52 
53 namespace
54 {
55  const std::string QUEUEMANAGER_PREFIX{"queue."};
56  const std::string SCHEDULER_PREFIX{"scheduler."};
57 }
58 
61  PlatformServiceProvider *pPlatformServiceProvider,
62  RadioServiceProvider * pRadioServiceProvider,
63  Scheduler * pScheduler,
64  QueueManager * pQueueManager,
65  MACLayerImplementor * pRadioModel):
66  MACLayerImplementor{id,pPlatformServiceProvider,pRadioServiceProvider},
67  pScheduler_{pScheduler},
68  pQueueManager_{pQueueManager},
69  pRadioModel_{pRadioModel},
70  bFlowControlEnable_{},
71  u16FlowControlTokens_{},
72  sPCRCurveURI_{},
73  transmitTimedEventId_{},
74  nextMultiFrameTime_{},
75  txSlotInfos_{},
76  slotDuration_{},
77  slotOverhead_{},
78  u64SequenceNumber_{},
79  frequencies_{},
80  u64BandwidthHz_{},
81  packetStatusPublisher_{},
82  neighborMetricManager_{id},
83  receiveManager_{id,
84  pRadioModel,
85  &pPlatformServiceProvider->logService(),
86  pRadioServiceProvider,
87  pScheduler,
88  &packetStatusPublisher_,
89  &neighborMetricManager_},
90  flowControlManager_{*pRadioModel},
91  u64ScheduleIndex_{}{}
92 
93 
95 {}
96 
97 
98 void
100 {
102  DEBUG_LEVEL,
103  "MACI %03hu TDMA::BaseModel::%s",
104  id_,
105  __func__);
106 
107  auto & configRegistrar = registrar.configurationRegistrar();
108 
109  configRegistrar.registerNumeric<bool>("enablepromiscuousmode",
112  {false},
113  "Defines whether promiscuous mode is enabled or not."
114  " If promiscuous mode is enabled, all received packets"
115  " (intended for the given node or not) that pass the"
116  " probability of reception check are sent upstream to"
117  " the transport.");
118 
119 
120  configRegistrar.registerNumeric<bool>("flowcontrolenable",
122  {false},
123  "Defines whether flow control is enabled. Flow control only works"
124  " with the virtual transport and the setting must match the setting"
125  " within the virtual transport configuration.");
126 
127  configRegistrar.registerNumeric<std::uint16_t>("flowcontroltokens",
129  {10},
130  "Defines the maximum number of flow control tokens"
131  " (packet transmission units) that can be processed from the"
132  " virtual transport without being refreshed. The number of"
133  " available tokens at any given time is coordinated with the"
134  " virtual transport and when the token count reaches zero, no"
135  " further packets are transmitted causing application socket"
136  " queues to backup.");
137 
138  configRegistrar.registerNonNumeric<std::string>("pcrcurveuri",
140  {},
141  "Defines the absolute URI of the Packet Completion Rate (PCR) curve"
142  " file. The PCR curve file contains probability of reception curves"
143  " as a function of Signal to Interference plus Noise Ratio (SINR).");
144 
145 
146  configRegistrar.registerNumeric<std::uint16_t>("fragmentcheckthreshold",
148  {2},
149  "Defines the rate in seconds a check is performed to see if any packet"
150  " fragment reassembly efforts should be abandoned.");
151 
152  configRegistrar.registerNumeric<std::uint16_t>("fragmenttimeoutthreshold",
154  {5},
155  "Defines the threshold in seconds to wait for another packet fragment"
156  " for an existing reassembly effort before abandoning the effort.");
157 
158  configRegistrar.registerNumeric<float>("neighbormetricdeletetime",
159  ConfigurationProperties::DEFAULT |
161  {60.0f},
162  "Defines the time in seconds of no RF receptions from a given neighbor"
163  " before it is removed from the neighbor table.",
164  1.0f,
165  3660.0f);
166 
167 
168  configRegistrar.registerNumeric<float>("neighbormetricupdateinterval",
170  {1.0f},
171  "Defines the neighbor table update interval in seconds.",
172  0.1f,
173  60.0f);
174 
175  auto & statisticRegistrar = registrar.statisticRegistrar();
176 
177  packetStatusPublisher_.registerStatistics(statisticRegistrar);
178 
179  slotStatusTablePublisher_.registerStatistics(statisticRegistrar);
180 
181  neighborMetricManager_.registerStatistics(statisticRegistrar);
182 
183  aggregationStatusPublisher_.registerStatistics(statisticRegistrar);
184 
185  pQueueManager_->setPacketStatusPublisher(&packetStatusPublisher_);
186 
187  pQueueManager_->initialize(registrar);
188 
189  pScheduler_->initialize(registrar);
190 }
191 
192 
193 
194 void
196 {
198  DEBUG_LEVEL,
199  "MACI %03hu TDMA::BaseModel::%s",
200  id_,
201  __func__);
202 
203  ConfigurationUpdate schedulerConfiguration{};
204  ConfigurationUpdate queueManagerConfiguration{};
205 
206  for(const auto & item : update)
207  {
208  if(item.first == "enablepromiscuousmode")
209  {
210  bool bPromiscuousMode{item.second[0].asBool()};
211 
213  INFO_LEVEL,
214  "MACI %03hu TDMA::BaseModel::%s: %s = %s",
215  id_,
216  __func__,
217  item.first.c_str(),
218  bPromiscuousMode ? "on" : "off");
219 
220  receiveManager_.setPromiscuousMode(bPromiscuousMode);
221  }
222  else if(item.first == "flowcontrolenable")
223  {
224  bFlowControlEnable_ = item.second[0].asBool();
225 
227  INFO_LEVEL,
228  "MACI %03hu TDMA::BaseModel::%s: %s = %s",
229  id_,
230  __func__,
231  item.first.c_str(),
232  bFlowControlEnable_ ? "on" : "off");
233  }
234  else if(item.first == "flowcontroltokens")
235  {
236  u16FlowControlTokens_ = item.second[0].asUINT16();
237 
239  INFO_LEVEL,
240  "MACI %03hu TDMA::BaseModel::%s: %s = %hu",
241  id_,
242  __func__,
243  item.first.c_str(),
244  u16FlowControlTokens_);
245  }
246  else if(item.first == "pcrcurveuri")
247  {
248  sPCRCurveURI_ = item.second[0].asString();
249 
251  INFO_LEVEL,
252  "MACI %03hu TDMA::BaseModel::%s: %s = %s",
253  id_,
254  __func__,
255  item.first.c_str(),
256  sPCRCurveURI_.c_str());
257 
258  receiveManager_.loadCurves(sPCRCurveURI_);
259  }
260  else if(item.first == "fragmentcheckthreshold")
261  {
262  std::chrono::seconds fragmentCheckThreshold{item.second[0].asUINT16()};
263 
265  INFO_LEVEL,
266  "MACI %03hu TDMA::BaseModel::%s: %s = %lu",
267  id_,
268  __func__,
269  item.first.c_str(),
270  fragmentCheckThreshold.count());
271 
272  receiveManager_.setFragmentCheckThreshold(fragmentCheckThreshold);
273  }
274  else if(item.first == "fragmenttimeoutthreshold")
275  {
276  std::chrono::seconds fragmentTimeoutThreshold{item.second[0].asUINT16()};
277 
279  INFO_LEVEL,
280  "MACI %03hu TDMA::BaseModel::%s: %s = %lu",
281  id_,
282  __func__,
283  item.first.c_str(),
284  fragmentTimeoutThreshold.count());
285 
286  receiveManager_.setFragmentTimeoutThreshold(fragmentTimeoutThreshold);
287  }
288  else if(item.first == "neighbormetricdeletetime")
289  {
290  Microseconds neighborMetricDeleteTimeMicroseconds =
291  std::chrono::duration_cast<Microseconds>(DoubleSeconds{item.second[0].asFloat()});
292 
293  // set the neighbor delete time
294  neighborMetricManager_.setNeighborDeleteTimeMicroseconds(neighborMetricDeleteTimeMicroseconds);
295 
297  DEBUG_LEVEL,
298  "MACI %03hu TDMA::BaseModel::%s %s = %lf",
299  id_,
300  __func__,
301  item.first.c_str(),
302  std::chrono::duration_cast<DoubleSeconds>(neighborMetricDeleteTimeMicroseconds).count());
303  }
304  else if(item.first == "neighbormetricupdateinterval")
305  {
306  neighborMetricUpdateInterval_ =
307  std::chrono::duration_cast<Microseconds>(DoubleSeconds{item.second[0].asFloat()});
308 
310  INFO_LEVEL,
311  "MACI %03hu TDMA::BaseModel::%s %s = %lf",
312  id_,
313  __func__,
314  item.first.c_str(),
315  std::chrono::duration_cast<DoubleSeconds>(neighborMetricUpdateInterval_).count());
316  }
317  else
318  {
319  if(!item.first.compare(0,SCHEDULER_PREFIX.size(),SCHEDULER_PREFIX))
320  {
321  schedulerConfiguration.push_back(item);
322  }
323  else if(!item.first.compare(0,QUEUEMANAGER_PREFIX.size(),QUEUEMANAGER_PREFIX))
324  {
325  queueManagerConfiguration.push_back(item);
326  }
327  else
328  {
329  throw makeException<ConfigureException>("TDMA::BaseModel: "
330  "Ambiguous configuration item %s.",
331  item.first.c_str());
332  }
333  }
334  }
335 
336  pQueueManager_->configure(queueManagerConfiguration);
337 
338  pScheduler_->configure(schedulerConfiguration);
339 }
340 
341 void
343 {
345  DEBUG_LEVEL,
346  "MACI %03hu TDMA::BaseModel::%s",
347  id_,
348  __func__);
349 
350  pQueueManager_->start();
351 
352  pScheduler_->start();
353 }
354 
355 
356 void
358 {
360  DEBUG_LEVEL,
361  "MACI %03hu TDMA::BaseModel::%s",
362  id_,
363  __func__);
364 
365  pQueueManager_->postStart();
366 
367  pScheduler_->postStart();
368 
369  // check flow control enabled
370  if(bFlowControlEnable_)
371  {
372  // start flow control
373  flowControlManager_.start(u16FlowControlTokens_);
374 
376  DEBUG_LEVEL,
377  "MACI %03hu TDMA::BaseModel::%s sent a flow control token update,"
378  " a handshake response is required to process packets",
379  id_,
380  __func__);
381  }
382 
385  &neighborMetricManager_),
386  Clock::now() + neighborMetricUpdateInterval_,
387  neighborMetricUpdateInterval_);
388 }
389 
390 
391 void
393 {
395  DEBUG_LEVEL,
396  "MACI %03hu TDMA::BaseModel::%s",
397  id_,
398  __func__);
399 
400  // check flow control enabled
401  if(bFlowControlEnable_)
402  {
403  // stop the flow control manager
404  flowControlManager_.stop();
405  }
406 
407  pQueueManager_->stop();
408 
409  pScheduler_->stop();
410 }
411 
412 
413 
414 void
416  throw()
417 {
419  DEBUG_LEVEL,
420  "MACI %03hu TDMA::BaseModel::%s",
421  id_,
422  __func__);
423 
424  pQueueManager_->destroy();
425 
426  pScheduler_->destroy();
427 }
428 
430 {
432  DEBUG_LEVEL,
433  "MACI %03hu TDMA::BaseModel::%s",
434  id_,
435  __func__);
436 
437 }
438 
439 
441  UpstreamPacket & pkt,
442  const ControlMessages & msgs)
443 {
444  auto now = Clock::now();
445 
447  DEBUG_LEVEL,
448  "MACI %03hu TDMA::BaseModel::%s",
449  id_,
450  __func__);
451 
452 
453  const PacketInfo & pktInfo{pkt.getPacketInfo()};
454 
456  {
458  ERROR_LEVEL,
459  "MACI %03hu TDMA::BaseModel::%s: MAC Registration Id %hu does not match our Id %hu, drop.",
460  id_,
461  __func__,
462  hdr.getRegistrationId(),
464 
465 
466  packetStatusPublisher_.inbound(pktInfo.getSource(),
467  pktInfo.getSource(),
468  pktInfo.getPriority(),
469  pkt.length(),
471 
472  // drop
473  return;
474  }
475 
476  size_t len{pkt.stripLengthPrefixFraming()};
477 
478  if(len && pkt.length() >= len)
479  {
480  BaseModelMessage baseModelMessage{pkt.get(), len};
481 
482 
483  const Controls::ReceivePropertiesControlMessage * pReceivePropertiesControlMessage{};
484 
485  const Controls::FrequencyControlMessage * pFrequencyControlMessage{};
486 
487  for(auto & pControlMessage : msgs)
488  {
489  switch(pControlMessage->getId())
490  {
492  {
493  pReceivePropertiesControlMessage =
494  static_cast<const Controls::ReceivePropertiesControlMessage *>(pControlMessage);
495 
497  DEBUG_LEVEL,
498  Controls::ReceivePropertiesControlMessageFormatter(pReceivePropertiesControlMessage),
499  "MACI %03hu TDMA::BaseModel::%s Receiver Properties Control Message",
500  id_,
501  __func__);
502  }
503  break;
504 
506  {
507  pFrequencyControlMessage =
508  static_cast<const Controls::FrequencyControlMessage *>(pControlMessage);
509 
511  DEBUG_LEVEL,
512  Controls::FrequencyControlMessageFormatter(pFrequencyControlMessage),
513  "MACI %03hu TDMA::BaseModel::%s Frequency Control Message",
514  id_,
515  __func__);
516 
517  }
518 
519  break;
520  }
521  }
522 
523  if(!pReceivePropertiesControlMessage || !pFrequencyControlMessage ||
524  pFrequencyControlMessage->getFrequencySegments().empty())
525  {
527  ERROR_LEVEL,
528  "MACI %03hu TDMA::BaseModel::%s: phy control "
529  "message not provided from src %hu, drop",
530  id_,
531  __func__,
532  pktInfo.getSource());
533 
534  packetStatusPublisher_.inbound(pktInfo.getSource(),
535  baseModelMessage.getMessages(),
537 
538  // drop
539  return;
540  }
541 
542  const auto & frequencySegments = pFrequencyControlMessage->getFrequencySegments();
543 
544  const FrequencySegment & frequencySegment{*frequencySegments.begin()};
545 
546  TimePoint startOfReception{pReceivePropertiesControlMessage->getTxTime() +
547  pReceivePropertiesControlMessage->getPropagationDelay() +
548  frequencySegment.getOffset()};
549 
550 
551  // if EOR slot does not match the SOT slot drop the packet
552  auto eorSlotInfo = pScheduler_->getSlotInfo(startOfReception +
553  frequencySegment.getDuration());
554 
555  // message is too long for slot
556  if(eorSlotInfo.u64AbsoluteSlotIndex_ != baseModelMessage.getAbsoluteSlotIndex())
557  {
558  // determine current slot based on now time to update rx slot status table
559  auto slotInfo = pScheduler_->getSlotInfo(now);
560 
561  Microseconds timeRemainingInSlot{};
562 
563  // ratio calcualtion for slot status tables
564  if(slotInfo.u64AbsoluteSlotIndex_ == baseModelMessage.getAbsoluteSlotIndex())
565  {
566  timeRemainingInSlot = slotDuration_ -
567  std::chrono::duration_cast<Microseconds>(now -
568  slotInfo.timePoint_);
569  }
570  else
571  {
572  timeRemainingInSlot = slotDuration_ +
573  std::chrono::duration_cast<Microseconds>(now -
574  slotInfo.timePoint_);
575  }
576 
577  double dSlotRemainingRatio =
578  timeRemainingInSlot.count() / static_cast<double>(slotDuration_.count());
579 
580  slotStatusTablePublisher_.update(slotInfo.u32RelativeIndex_,
581  slotInfo.u32RelativeFrameIndex_,
582  slotInfo.u32RelativeSlotIndex_,
584  dSlotRemainingRatio);
585 
586  packetStatusPublisher_.inbound(pktInfo.getSource(),
587  baseModelMessage.getMessages(),
589 
590 
592  DEBUG_LEVEL,
593  "MACI %03hu TDMA::BaseModel::%s eor rx slot:"
594  " %zu does not match sot slot: %zu, drop long",
595  id_,
596  __func__,
597  eorSlotInfo.u64AbsoluteSlotIndex_,
598  baseModelMessage.getAbsoluteSlotIndex());
599 
600  // drop
601  return;
602  }
603 
604  // rx slot info for now
605  auto entry = pScheduler_->getRxSlotInfo(now);
606 
607  if(entry.first.u64AbsoluteSlotIndex_ == baseModelMessage.getAbsoluteSlotIndex())
608  {
609  Microseconds timeRemainingInSlot{slotDuration_ -
610  std::chrono::duration_cast<Microseconds>(now -
611  entry.first.timePoint_)};
612 
613  double dSlotRemainingRatio =
614  timeRemainingInSlot.count() / static_cast<double>(slotDuration_.count());
615 
616  if(entry.second)
617  {
618  if(entry.first.u64FrequencyHz_ == frequencySegment.getFrequencyHz())
619  {
620  // we are in an RX Slot
621  slotStatusTablePublisher_.update(entry.first.u32RelativeIndex_,
622  entry.first.u32RelativeFrameIndex_,
623  entry.first.u32RelativeSlotIndex_,
625  dSlotRemainingRatio);
626 
627  Microseconds span{pReceivePropertiesControlMessage->getSpan()};
628 
629  if(receiveManager_.enqueue(std::move(baseModelMessage),
630  pktInfo,
631  pkt.length(),
632  startOfReception,
633  frequencySegments,
634  span,
635  now,
636  hdr.getSequenceNumber()))
637  {
639  schedule(std::bind(&ReceiveManager::process,
640  &receiveManager_,
641  entry.first.u64AbsoluteSlotIndex_+1),
642  entry.first.timePoint_+ slotDuration_);
643  }
644  }
645  else
646  {
647  slotStatusTablePublisher_.update(entry.first.u32RelativeIndex_,
648  entry.first.u32RelativeFrameIndex_,
649  entry.first.u32RelativeSlotIndex_,
651  dSlotRemainingRatio);
652 
653  packetStatusPublisher_.inbound(pktInfo.getSource(),
654  baseModelMessage.getMessages(),
656 
658  DEBUG_LEVEL,
659  "MACI %03hu TDMA::BaseModel::%s drop reason rx slot correct"
660  " rframe: %u rslot: %u but frequency mismatch expected: %zu got: %zu",
661  id_,
662  __func__,
663  entry.first.u32RelativeFrameIndex_,
664  entry.first.u32RelativeSlotIndex_,
665  entry.first.u64FrequencyHz_,
666  frequencySegment.getFrequencyHz());
667 
668  // drop
669  return;
670  }
671  }
672  else
673  {
674  // not an rx slot but it is the correct abs slot
675  auto slotInfo = pScheduler_->getSlotInfo(entry.first.u64AbsoluteSlotIndex_);
676 
677  slotStatusTablePublisher_.update(entry.first.u32RelativeIndex_,
678  entry.first.u32RelativeFrameIndex_,
679  entry.first.u32RelativeSlotIndex_,
680  slotInfo.type_ == SlotInfo::Type::IDLE ?
683  dSlotRemainingRatio);
684 
685  packetStatusPublisher_.inbound(pktInfo.getSource(),
686  baseModelMessage.getMessages(),
688 
689 
691  DEBUG_LEVEL,
692  "MACI %03hu TDMA::BaseModel::%s drop reason rx slot correct but %s rframe: %u rslot: %u",
693  id_,
694  __func__,
695  slotInfo.type_ == SlotInfo::Type::IDLE ?
696  "in idle" : "in tx",
697  entry.first.u32RelativeFrameIndex_,
698  entry.first.u32RelativeSlotIndex_);
699 
700 
701  // drop
702  return;
703  }
704  }
705  else
706  {
707  auto slotInfo = pScheduler_->getSlotInfo(entry.first.u64AbsoluteSlotIndex_);
708 
709  Microseconds timeRemainingInSlot{slotDuration_ +
710  std::chrono::duration_cast<Microseconds>(now -
711  slotInfo.timePoint_)};
712  double dSlotRemainingRatio =
713  timeRemainingInSlot.count() / static_cast<double>(slotDuration_.count());
714 
715 
716  // were we supposed to be in rx on the pkt abs slot
717  if(slotInfo.type_ == SlotInfo::Type::RX)
718  {
719  slotStatusTablePublisher_.update(entry.first.u32RelativeIndex_,
720  entry.first.u32RelativeFrameIndex_,
721  entry.first.u32RelativeSlotIndex_,
723  dSlotRemainingRatio);
724 
725  packetStatusPublisher_.inbound(pktInfo.getSource(),
726  baseModelMessage.getMessages(),
728 
730  DEBUG_LEVEL,
731  "MACI %03hu TDMA::BaseModel::%s drop reason slot mismatch pkt: %zu now: %zu ",
732  id_,
733  __func__,
734  baseModelMessage.getAbsoluteSlotIndex(),
735  entry.first.u64AbsoluteSlotIndex_);
736 
737  // drop
738  return;
739  }
740  else
741  {
742  slotStatusTablePublisher_.update(entry.first.u32RelativeIndex_,
743  entry.first.u32RelativeFrameIndex_,
744  entry.first.u32RelativeSlotIndex_,
745  slotInfo.type_ == SlotInfo::Type::IDLE ?
748  dSlotRemainingRatio);
749 
750  packetStatusPublisher_.inbound(pktInfo.getSource(),
751  baseModelMessage.getMessages(),
753 
754 
756  DEBUG_LEVEL,
757  "MACI %03hu TDMA::BaseModel::%s drop reason slot mismatch but %s pkt: %zu now: %zu ",
758  id_,
759  __func__,
760  slotInfo.type_ == SlotInfo::Type::IDLE ?
761  "in idle" : "in tx",
762  baseModelMessage.getAbsoluteSlotIndex(),
763  entry.first.u64AbsoluteSlotIndex_);
764 
765  // drop
766  return;
767  }
768  }
769  }
770  else
771  {
773  ERROR_LEVEL,
774  "MACI %03hu TDMA::BaseModel::%s Packet payload length %zu does not match length prefix %zu",
775  id_,
776  __func__,
777  pkt.length(),
778  len);
779  }
780 }
781 
783 {
785  DEBUG_LEVEL,
786  "MACI %03hu TDMA::BaseModel::%s",
787  id_,
788  __func__);
789 
790  for(const auto & pMessage : msgs)
791  {
793  DEBUG_LEVEL,
794  "MACI %03hu TDMA::BaseModel::%s downstream control message id %hu",
795  id_,
796  __func__,
797  pMessage->getId());
798 
799  switch(pMessage->getId())
800  {
802  {
803  const auto pFlowControlControlMessage =
804  static_cast<const Controls::FlowControlControlMessage *>(pMessage);
805 
806  if(bFlowControlEnable_)
807  {
809  DEBUG_LEVEL,
810  "MACI %03hu TDMA::BaseModel::%s received a flow control token request/response",
811  id_,
812  __func__);
813 
814  flowControlManager_.processFlowControlMessage(pFlowControlControlMessage);
815  }
816  else
817  {
819  ERROR_LEVEL,
820  "MACI %03hu TDMA::BaseModel::%s received a flow control token request but"
821  " flow control is not enabled",
822  id_,
823  __func__);
824  }
825  }
826  break;
827 
829  {
830  const auto pSerializedControlMessage =
831  static_cast<const Controls::SerializedControlMessage *>(pMessage);
832 
833  switch(pSerializedControlMessage->getSerializedId())
834  {
836  {
837  std::unique_ptr<Controls::FlowControlControlMessage>
838  pFlowControlControlMessage{
839  Controls::FlowControlControlMessage::create(pSerializedControlMessage->getSerialization())};
840 
841  if(bFlowControlEnable_)
842  {
843  flowControlManager_.processFlowControlMessage(pFlowControlControlMessage.get());
844  }
845  else
846  {
848  ERROR_LEVEL,
849  "MACI %03hu TDMA::BaseModel::%s received a flow control token request but"
850  " flow control is not enabled",
851  id_,
852  __func__);
853  }
854  }
855  break;
856  }
857  }
858  }
859  }
860 }
861 
862 
864  const ControlMessages &)
865 {
867  DEBUG_LEVEL,
868  "MACI %03hu TDMA::BaseModel::%s",
869  id_,
870  __func__);
871 
872 
873  // check flow control
874  if(bFlowControlEnable_)
875  {
876  auto status = flowControlManager_.removeToken();
877 
878  if(status.second == false)
879  {
881  ERROR_LEVEL,
882  "MACI %03hu TDMA::BaseModel::%s: failed to remove token, drop packet (tokens:%hu)",
883  id_,
884  __func__,
885  status.first);
886 
887  const auto & pktInfo = pkt.getPacketInfo();
888 
889  packetStatusPublisher_.outbound(pktInfo.getSource(),
890  pktInfo.getSource(),
891  pktInfo.getPriority(),
892  pkt.length(),
894 
895  // drop
896  return;
897  }
898  }
899 
900  std::uint8_t u8Queue{priorityToQueue(pkt.getPacketInfo().getPriority())};
901 
902  size_t packetsDropped{pQueueManager_->enqueue(u8Queue,std::move(pkt))};
903 
904  // drop, replace token
905  if(bFlowControlEnable_)
906  {
907  for(size_t i = 0; i < packetsDropped; ++i)
908  {
909  auto status = flowControlManager_.addToken();
910 
911  if(!status.second)
912  {
914  ERROR_LEVEL,
915  "MACI %03hu TDMA::BaseModel:::%s: failed to add token (tokens:%hu)",
916  id_,
917  __func__,
918  status.first);
919  }
920  }
921  }
922 }
923 
924 
926  const Serialization & serialization)
927 {
929  DEBUG_LEVEL,
930  "MACI %03hu TDMA::BaseModel::%s",
931  id_,
932  __func__);
933 
934  pScheduler_->processEvent(eventId,serialization);
935 
936 }
937 
938 
940 {
942  DEBUG_LEVEL,
943  "MACI %03hu TDMA::BaseModel::%s",
944  id_,
945  __func__);
946 
947 
948  ConfigurationUpdate schedulerConfiguration;
949 
950  for(const auto & item : update)
951  {
952  if(item.first == "enablepromiscuousmode")
953  {
954  bool bPromiscuousMode{item.second[0].asBool()};
955 
957  INFO_LEVEL,
958  "MACI %03hu TDMA::BaseModel::%s: %s = %s",
959  id_,
960  __func__,
961  item.first.c_str(),
962  bPromiscuousMode ? "on" : "off");
963 
964  receiveManager_.setPromiscuousMode(bPromiscuousMode);
965  }
966  else
967  {
968  schedulerConfiguration.push_back(item);
969  }
970  }
971 
972  pScheduler_->configure(schedulerConfiguration);
973 }
974 
976  std::uint64_t u64BandwidthHz,
977  const Microseconds & slotDuration,
978  const Microseconds & slotOverhead)
979 {
981  DEBUG_LEVEL,
982  "MACI %03hu TDMA::BaseModel::%s",
983  id_,
984  __func__);
985 
986  // increment index to indicate a schedule change
987  ++u64ScheduleIndex_;
988 
989  if(transmitTimedEventId_)
990  {
991  pPlatformService_->timerService().cancelTimedEvent(transmitTimedEventId_);
992  transmitTimedEventId_ = 0;
993  }
994 
995  if(u64BandwidthHz_ != u64BandwidthHz || frequencies != frequencies_)
996  {
997  // only required if freq set/bandwidth differs from existing
998  pRadioModel_->sendDownstreamControl({Controls::FrequencyOfInterestControlMessage::create(u64BandwidthHz,frequencies)});
999 
1000  frequencies_ = frequencies;
1001 
1002  u64BandwidthHz_ = u64BandwidthHz;
1003  }
1004 
1005  slotDuration_ = slotDuration;
1006 
1007  slotOverhead_ = slotOverhead;
1008 
1009  slotStatusTablePublisher_.clear();
1010 
1011  std::tie(txSlotInfos_,
1012  nextMultiFrameTime_) =
1013  pScheduler_->getTxSlotInfo(Clock::now(),1);
1014 
1016  DEBUG_LEVEL,
1017  TxSlotInfosFormatter(txSlotInfos_),
1018  "MACI %03hu TDMA::BaseModel::%s TX Slot Info",
1019  id_,
1020  __func__);
1021 
1022 
1023 
1024  if(!txSlotInfos_.empty())
1025  {
1026  pendingTxSlotInfo_ = *txSlotInfos_.begin();
1027 
1028  txSlotInfos_.pop_front();
1029 
1030  transmitTimedEventId_ =
1032  schedule(std::bind(&Implementation::processTxOpportunity,
1033  this,
1034  u64ScheduleIndex_),
1035  pendingTxSlotInfo_.timePoint_);
1036  }
1037 }
1038 
1040 {
1042  DEBUG_LEVEL,
1043  "MACI %03hu TDMA::BaseModel::%s",
1044  id_,
1045  __func__);
1046 
1047  // enqueue into max priority queue
1048  pQueueManager_->enqueue(4,std::move(pkt));
1049 }
1050 
1051 
1053 {
1055  DEBUG_LEVEL,
1056  "MACI %03hu TDMA::BaseModel::%s",
1057  id_,
1058  __func__);
1059 
1060  pRadioModel_->sendDownstreamControl(msgs);
1061 }
1062 
1063 
1065 {
1067  DEBUG_LEVEL,
1068  "MACI %03hu TDMA::BaseModel::%s",
1069  id_,
1070  __func__);
1071 
1072  return pQueueManager_->getPacketQueueInfo();
1073 }
1074 
1075 void EMANE::Models::TDMA::BaseModel::Implementation::sendDownstreamPacket(double dSlotRemainingRatio)
1076 {
1077  // calculate the number of bytes allowed in the slot
1078  size_t bytesAvailable =
1079  (slotDuration_.count() - slotOverhead_.count()) / 1000000.0 * pendingTxSlotInfo_.u64DataRatebps_ / 8.0;
1080 
1081  auto entry = pQueueManager_->dequeue(pendingTxSlotInfo_.u8QueueId_,
1082  bytesAvailable,
1083  pendingTxSlotInfo_.destination_);
1084 
1085  MessageComponents & components = std::get<0>(entry);
1086  size_t totalSize{std::get<1>(entry)};
1087 
1088  if(totalSize)
1089  {
1090  if(totalSize <= bytesAvailable)
1091  {
1092  float fSeconds{totalSize * 8.0f / pendingTxSlotInfo_.u64DataRatebps_};
1093 
1094  Microseconds duration{std::chrono::duration_cast<Microseconds>(DoubleSeconds{fSeconds})};
1095 
1096  // rounding error corner case mitigation
1097  if(duration >= slotDuration_)
1098  {
1099  duration = slotDuration_ - Microseconds{1};
1100  }
1101 
1102  NEMId dst{};
1103  size_t completedPackets{};
1104 
1105  // determine how many components represent completed packets (no fragments remain) and
1106  // whether to use a unicast or broadcast nem address
1107  for(const auto & component : components)
1108  {
1109  completedPackets += !component.isMoreFragments();
1110 
1111  // if not set, set a destination
1112  if(!dst)
1113  {
1114  dst = component.getDestination();
1115  }
1116  else if(dst != NEM_BROADCAST_MAC_ADDRESS)
1117  {
1118  // if the destination is not broadcast, check to see if it matches
1119  // the destination of the current component - if not, set the NEM
1120  // broadcast address as the dst
1121  if(dst != component.getDestination())
1122  {
1124  }
1125  }
1126  }
1127 
1128 
1130  DEBUG_LEVEL,
1131  "MACI %03hu TDMA::BaseModel::%s sending downstream to %03hu components: %zu",
1132  id_,
1133  __func__,
1134  dst,
1135  components.size());
1136 
1137 
1138  if(bFlowControlEnable_ && completedPackets)
1139  {
1140  auto status = flowControlManager_.addToken(completedPackets);
1141 
1142  if(!status.second)
1143  {
1145  ERROR_LEVEL,
1146  "MACI %03hu TDMA::BaseModel::%s: failed to add token (tokens:%hu)",
1147  id_,
1148  __func__,
1149  status.first);
1150 
1151  }
1152  }
1153 
1154  aggregationStatusPublisher_.update(components);
1155 
1156  BaseModelMessage baseModelMessage{pendingTxSlotInfo_.u64AbsoluteSlotIndex_,
1157  pendingTxSlotInfo_.u64DataRatebps_,
1158  std::move(components)};
1159 
1160  Serialization serialization{baseModelMessage.serialize()};
1161 
1162  auto now = Clock::now();
1163 
1164  DownstreamPacket pkt({id_,dst,0,now},serialization.c_str(),serialization.size());
1165 
1166  pkt.prependLengthPrefixFraming(serialization.size());
1167 
1168  pRadioModel_->sendDownstreamPacket(CommonMACHeader{REGISTERED_EMANE_MAC_TDMA,u64SequenceNumber_++},
1169  pkt,
1171  u64BandwidthHz_,
1172  {{pendingTxSlotInfo_.u64FrequencyHz_,duration}}),
1175 
1176  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1177  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1178  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1180  dSlotRemainingRatio);
1181 
1182  neighborMetricManager_.updateNeighborTxMetric(dst,
1183  pendingTxSlotInfo_.u64DataRatebps_,
1184  now);
1185  }
1186  else
1187  {
1189  ERROR_LEVEL,
1190  "MACI %03hu TDMA::BaseModel::%s queue dequeue returning %zu bytes than slot has available %zu",
1191  id_,
1192  __func__,
1193  totalSize,
1194  bytesAvailable);
1195  }
1196  }
1197  else
1198  {
1199  // nothing to transmit, update the slot table to record how well
1200  // schedule is being serviced
1201  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1202  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1203  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1205  dSlotRemainingRatio);
1206  }
1207 }
1208 
1209 void EMANE::Models::TDMA::BaseModel::Implementation::processTxOpportunity(std::uint64_t u64ScheduleIndex)
1210 {
1211  // check for scheduled timer functor after new schedule, if so disregard
1212  if(u64ScheduleIndex != u64ScheduleIndex_)
1213  {
1215  ERROR_LEVEL,
1216  "MACI %03hu TDMA::BaseModel::%s old schedule tx opportunity found"
1217  " scheduled index: %zu current index: %zu",
1218  id_,
1219  __func__,
1220  u64ScheduleIndex,
1221  u64ScheduleIndex_);
1222  return;
1223  }
1224 
1225  auto now = Clock::now();
1226 
1227  auto nowSlotInfo = pScheduler_->getSlotInfo(now);
1228 
1229  Microseconds timeRemainingInSlot{slotDuration_ -
1230  std::chrono::duration_cast<Microseconds>(now -
1231  pendingTxSlotInfo_.timePoint_)};
1232  double dSlotRemainingRatio =
1233  timeRemainingInSlot.count() / static_cast<double>(slotDuration_.count());
1234 
1235  if(nowSlotInfo.u64AbsoluteSlotIndex_ == pendingTxSlotInfo_.u64AbsoluteSlotIndex_)
1236  {
1237  // transmit in this slot
1238  sendDownstreamPacket(dSlotRemainingRatio);
1239  }
1240  else
1241  {
1242  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1243  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1244  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1246  dSlotRemainingRatio);
1247  }
1248 
1249 
1250  // if necessary request more tx opportunities
1251  if(txSlotInfos_.empty())
1252  {
1253  // request more slots
1254  std::tie(txSlotInfos_,
1255  nextMultiFrameTime_) =
1256  pScheduler_->getTxSlotInfo(nextMultiFrameTime_,1);
1257  }
1258 
1259  bool bFoundTXSlot = {};
1260 
1261  // find the next transmit opportunity
1262  while(!txSlotInfos_.empty() && !bFoundTXSlot)
1263  {
1264  // it might be necessary to request more opportunies if we
1265  // are behind and have many tx opportunities that have past
1266  while(!txSlotInfos_.empty())
1267  {
1268  pendingTxSlotInfo_ = *txSlotInfos_.begin();
1269 
1270  txSlotInfos_.pop_front();
1271 
1272  if(pendingTxSlotInfo_.u64AbsoluteSlotIndex_ > nowSlotInfo.u64AbsoluteSlotIndex_)
1273  {
1274  // need to schedule processing in the future
1275 
1276  transmitTimedEventId_ =
1278  schedule(std::bind(&Implementation::processTxOpportunity,
1279  this,
1280  u64ScheduleIndex_),
1281  pendingTxSlotInfo_.timePoint_);
1282 
1283  bFoundTXSlot = true;
1284  break;
1285  }
1286  else if(pendingTxSlotInfo_.u64AbsoluteSlotIndex_ < nowSlotInfo.u64AbsoluteSlotIndex_)
1287  {
1288  // missed opportunity
1289  double dSlotRemainingRatio = \
1290  std::chrono::duration_cast<Microseconds>(pendingTxSlotInfo_.timePoint_ - now).count() /
1291  static_cast<double>(slotDuration_.count());
1292 
1293  // blown tx opportunity
1294  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1295  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1296  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1298  dSlotRemainingRatio);
1299  }
1300  else
1301  {
1302  // send the packet
1303  timeRemainingInSlot = slotDuration_ -
1304  std::chrono::duration_cast<Microseconds>(now -
1305  pendingTxSlotInfo_.timePoint_);
1306 
1307 
1308  double dSlotRemainingRatio =
1309  timeRemainingInSlot.count() / static_cast<double>(slotDuration_.count());
1310 
1311 
1312  sendDownstreamPacket(dSlotRemainingRatio);
1313  }
1314  }
1315 
1316  // if we are out of slots
1317  if(txSlotInfos_.empty())
1318  {
1319  // request more slots
1320  std::tie(txSlotInfos_,
1321  nextMultiFrameTime_) =
1322  pScheduler_->getTxSlotInfo(nextMultiFrameTime_,1);
1323  }
1324  }
1325 
1326  return;
1327 }
void processSchedulerPacket(DownstreamPacket &pkt) override
std::string Serialization
Definition: serializable.h:42
A Packet class that allows upstream processing to strip layer headers as the packet travels up the st...
const PacketInfo & getPacketInfo() const
The Registrar interface provides access to all of the emulator registrars.
Definition: registrar.h:50
A Serialized Control Message is used to encapsulate Serializable control messages as they traverse pr...
void outbound(NEMId src, NEMId dst, Priority priority, size_t size, OutboundAction action) override
Implementation(NEMId id, PlatformServiceProvider *pPlatformServiceProvider, RadioServiceProvider *pRadioServiceProvider, Scheduler *pScheduler, QueueManager *pQueueManager, MACLayerImplementor *pRadioModel)
virtual ConfigurationRegistrar & configurationRegistrar()=0
void registerStatistics(StatisticRegistrar &registrar)
void inbound(NEMId src, const MessageComponent &component, InboundAction action) override
void process(std::uint64_t u64AbsoluteSlotIndex)
Message class used to serialize and deserialize TDMA radio model messages.
virtual StatisticRegistrar & statisticRegistrar()=0
Recieve Properties Control Message is sent from the emulator physical layer with every upstream packe...
Holds the frequency, offset and duration of a frequency segment.
void update(const MessageComponents &components)
static TransmitterControlMessage * create(const Transmitters &transmitters)
std::list< const ControlMessage * > ControlMessages
std::pair< std::uint16_t, bool > addToken(std::uint16_t u16Tokens=1)
static TimeStampControlMessage * create(const TimePoint &timestamp)
RegistrationId getRegistrationId() const
Callable formatter object for TxSlotInfos instances.
std::uint16_t stripLengthPrefixFraming()
constexpr NEMId NEM_BROADCAST_MAC_ADDRESS
Definition: types.h:69
union EtherAddr dst
Definition: netutils.h:390
void sendDownstreamControl(const ControlMessages &msgs)
void start(std::uint16_t u16TotalTokensAvailable)
void initialize(Registrar &registrar) override
std::uint16_t EventId
Definition: types.h:53
The PlatformServiceProvider interface provides access to emulator services.
static FlowControlControlMessage * create(const Serialization &serialization)
void updateNeighborTxMetric(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint &txTime)
Store source, destination, creation time and priority information for a packet.
Definition: packetinfo.h:50
static FrequencyControlMessage * create(std::uint64_t u64BandwidthHz, const FrequencySegments &frequencySegments)
void processConfiguration(const ConfigurationUpdate &update) override
The Frequency Control Message is sent to the emulator physical layer to specify the frequency segment...
std::vector< QueueInfo > QueueInfos
void processDownstreamPacket(DownstreamPacket &pkt, const ControlMessages &msgs) override
void processFlowControlMessage(const Controls::FlowControlControlMessage *pMsg)
#define LOGGER_VERBOSE_LOGGING_FN_VARGS(logger, level, fn, fmt, args...)
const PacketInfo & getPacketInfo() const
void setFragmentTimeoutThreshold(const std::chrono::seconds &threshold)
Specialized packet the allows downstream processing to add layer specific headers as the packet trave...
void update(std::uint32_t u32RelativeIndex, std::uint32_t u32RelativeFrameIndex, std::uint32_t u32RelativeSlotIndex, Status status, double dSlotRemainingRatio)
std::list< MessageComponent > MessageComponents
std::set< std::uint64_t > Frequencies
void registerStatistics(StatisticRegistrar &registrar)
std::chrono::microseconds Microseconds
Definition: types.h:45
void loadCurves(const std::string &sPCRFileName)
std::chrono::duration< double > DoubleSeconds
Definition: types.h:47
std::uint16_t NEMId
Definition: types.h:52
Callable formatter object for ReceivePropertiesControlMessage instances.
const Microseconds & getOffset() const
Flow Control Control Messages are sent between a MAC layer and a transport in order to communicate da...
PlatformServiceProvider * pPlatformService_
The RadioServiceProvider interface provides access to radio (RF) model specific services.
void processDownstreamControl(const ControlMessages &msgs) override
void sendDownstreamPacket(const CommonMACHeader &hdr, DownstreamPacket &pkt, const ControlMessages &msgs=DownstreamTransport::empty)
Scheduler interface used by BaseModel to communicate with a scheduler module.
Definition: scheduler.h:56
static FrequencyOfInterestControlMessage * create(std::uint64_t u64BandwidthHz, const FrequencySet &frequencySet)
void setFragmentCheckThreshold(const std::chrono::seconds &threshold)
std::uint64_t getSequenceNumber() const
void registerStatistics(StatisticRegistrar &statisticRegistrar)
void setNeighborDeleteTimeMicroseconds(const Microseconds &ageMicroseconds)
Priority getPriority() const
Definition: packetinfo.inl:76
const void * get() const
virtual LogServiceProvider & logService()=0
void configure(const ConfigurationUpdate &update) override
Queue management interface used by BaseModel
Definition: queuemanager.h:57
Callable formatter object for FrequencyControlMessage instances.
std::vector< ConfigurationNameAnyValues > ConfigurationUpdate
void notifyScheduleChange(const Frequencies &frequencies, std::uint64_t u64BandwidthHz, const Microseconds &slotDuration, const Microseconds &slotOverhead) override
std::pair< std::uint16_t, bool > removeToken()
void registerStatistics(StatisticRegistrar &registrar)
std::uint8_t priorityToQueue(Priority priority)
Definition: priority.h:53
void registerNumeric(const std::string &sName, const ConfigurationProperties &properties=ConfigurationProperties::NONE, const std::initializer_list< T > &values={}, const std::string &sUsage="", T minValue=std::numeric_limits< T >::lowest(), T maxValue=std::numeric_limits< T >::max(), std::size_t minOccurs=1, std::size_t maxOccurs=1, const std::string &sRegexPattern={})
void processSchedulerControl(const ControlMessages &msgs) override
QueueInfos getPacketQueueInfo() const override
Clock::time_point TimePoint
Definition: types.h:50
virtual bool cancelTimedEvent(TimerEventId eventId)=0
void prependLengthPrefixFraming(std::uint16_t u16Length)
#define LOGGER_STANDARD_LOGGING(logger, level, fmt, args...)
void processUpstreamPacket(const CommonMACHeader &hdr, UpstreamPacket &pkt, const ControlMessages &msgs) override
bool enqueue(BaseModelMessage &&baseModelMessage, const PacketInfo &pktInfo, size_t length, const TimePoint &startOfReception, const FrequencySegments &frequencySegments, const Microseconds &span, const TimePoint &beginTime, std::uint64_t u64PacketSequence)
const RegistrationId REGISTERED_EMANE_MAC_TDMA
Definition: mactypes.h:70
void processUpstreamControl(const ControlMessages &msgs) override
void processEvent(const EventId &, const Serialization &) override
Interface used to create a MAC layer plugin implementation.
Definition: maclayerimpl.h:48
virtual TimerServiceProvider & timerService()=0