EMANE  1.2.1
basemodelimpl.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2018 - 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 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  slotStatusTablePublisher_.update(slotInfo.u32RelativeIndex_,
562  slotInfo.u32RelativeFrameIndex_,
563  slotInfo.u32RelativeSlotIndex_,
565  slotPortionRatio(now,slotInfo.timePoint_));
566 
567  packetStatusPublisher_.inbound(pktInfo.getSource(),
568  baseModelMessage.getMessages(),
570 
571 
573  DEBUG_LEVEL,
574  "MACI %03hu TDMA::BaseModel::%s eor rx slot:"
575  " %zu does not match sot slot: %zu, drop long",
576  id_,
577  __func__,
578  eorSlotInfo.u64AbsoluteSlotIndex_,
579  baseModelMessage.getAbsoluteSlotIndex());
580 
581  // drop
582  return;
583  }
584 
585  // rx slot info for now
586  auto nowSlotInfoEntry = pScheduler_->getRxSlotInfo(now);
587 
588  if(nowSlotInfoEntry.first.u64AbsoluteSlotIndex_ == baseModelMessage.getAbsoluteSlotIndex())
589  {
590  if(nowSlotInfoEntry.second)
591  {
592  if(nowSlotInfoEntry.first.u64FrequencyHz_ == frequencySegment.getFrequencyHz())
593  {
594  Microseconds span{pReceivePropertiesControlMessage->getSpan()};
595 
596  if(receiveManager_.enqueue(std::move(baseModelMessage),
597  pktInfo,
598  pkt.length(),
599  startOfReception,
600  frequencySegments,
601  span,
602  now,
603  hdr.getSequenceNumber()))
604  {
606  schedule(std::bind(&ReceiveManager::process,
607  &receiveManager_,
608  nowSlotInfoEntry.first.u64AbsoluteSlotIndex_+1),
609  nowSlotInfoEntry.first.timePoint_+ slotDuration_);
610 
611  // we have a good Rx
612  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
613  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
614  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
616  slotPortionRatio(now,
617  nowSlotInfoEntry.first.timePoint_));
618  }
619  else
620  {
621  // enqueue of false indicates there is already a
622  // pending packet on this rx slot, so either
623  // this packet or the previous one has been
624  // discarded depending on which has earlier
625  // start-of-reception
626  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
627  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
628  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
630  slotPortionRatio(now,
631  nowSlotInfoEntry.first.timePoint_));
632  }
633  }
634  else
635  {
636  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
637  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
638  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
640  slotPortionRatio(now,
641  nowSlotInfoEntry.first.timePoint_));
642 
643  packetStatusPublisher_.inbound(pktInfo.getSource(),
644  baseModelMessage.getMessages(),
646 
648  DEBUG_LEVEL,
649  "MACI %03hu TDMA::BaseModel::%s drop reason rx slot correct"
650  " rframe: %u rslot: %u but frequency mismatch expected: %zu got: %zu",
651  id_,
652  __func__,
653  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
654  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
655  nowSlotInfoEntry.first.u64FrequencyHz_,
656  frequencySegment.getFrequencyHz());
657 
658  // drop
659  return;
660  }
661  }
662  else
663  {
664  // not an rx slot but it is the correct abs slot
665  auto slotInfo = pScheduler_->getSlotInfo(nowSlotInfoEntry.first.u64AbsoluteSlotIndex_);
666 
667  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
668  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
669  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
670  slotInfo.type_ == SlotInfo::Type::IDLE ?
673  slotPortionRatio(now,
674  nowSlotInfoEntry.first.timePoint_));
675 
676  packetStatusPublisher_.inbound(pktInfo.getSource(),
677  baseModelMessage.getMessages(),
679 
680 
682  DEBUG_LEVEL,
683  "MACI %03hu TDMA::BaseModel::%s drop reason rx slot correct but %s rframe: %u rslot: %u",
684  id_,
685  __func__,
686  slotInfo.type_ == SlotInfo::Type::IDLE ?
687  "in idle" : "in tx",
688  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
689  nowSlotInfoEntry.first.u32RelativeSlotIndex_);
690 
691 
692  // drop
693  return;
694  }
695  }
696  else
697  {
698  auto slotInfo = pScheduler_->getSlotInfo(baseModelMessage.getAbsoluteSlotIndex());
699 
700  // were we supposed to be in rx on the pkt abs slot
701  if(slotInfo.type_ == SlotInfo::Type::RX)
702  {
703  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
704  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
705  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
707  slotPortionRatio(now,
708  slotInfo.timePoint_));
709 
710  packetStatusPublisher_.inbound(pktInfo.getSource(),
711  baseModelMessage.getMessages(),
713 
715  DEBUG_LEVEL,
716  "MACI %03hu TDMA::BaseModel::%s drop reason slot mismatch pkt: %zu now: %zu ",
717  id_,
718  __func__,
719  baseModelMessage.getAbsoluteSlotIndex(),
720  nowSlotInfoEntry.first.u64AbsoluteSlotIndex_);
721 
722  // drop
723  return;
724  }
725  else
726  {
727  slotStatusTablePublisher_.update(nowSlotInfoEntry.first.u32RelativeIndex_,
728  nowSlotInfoEntry.first.u32RelativeFrameIndex_,
729  nowSlotInfoEntry.first.u32RelativeSlotIndex_,
730  slotInfo.type_ == SlotInfo::Type::IDLE ?
733  slotPortionRatio(now,
734  slotInfo.timePoint_));
735 
736  packetStatusPublisher_.inbound(pktInfo.getSource(),
737  baseModelMessage.getMessages(),
739 
740 
742  DEBUG_LEVEL,
743  "MACI %03hu TDMA::BaseModel::%s drop reason slot mismatch but %s pkt: %zu now: %zu ",
744  id_,
745  __func__,
746  slotInfo.type_ == SlotInfo::Type::IDLE ?
747  "in idle" : "in tx",
748  baseModelMessage.getAbsoluteSlotIndex(),
749  nowSlotInfoEntry.first.u64AbsoluteSlotIndex_);
750 
751  // drop
752  return;
753  }
754  }
755  }
756  else
757  {
759  ERROR_LEVEL,
760  "MACI %03hu TDMA::BaseModel::%s Packet payload length %zu does not match length prefix %zu",
761  id_,
762  __func__,
763  pkt.length(),
764  len);
765  }
766 }
767 
769 {
771  DEBUG_LEVEL,
772  "MACI %03hu TDMA::BaseModel::%s",
773  id_,
774  __func__);
775 
776  for(const auto & pMessage : msgs)
777  {
779  DEBUG_LEVEL,
780  "MACI %03hu TDMA::BaseModel::%s downstream control message id %hu",
781  id_,
782  __func__,
783  pMessage->getId());
784 
785  switch(pMessage->getId())
786  {
788  {
789  const auto pFlowControlControlMessage =
790  static_cast<const Controls::FlowControlControlMessage *>(pMessage);
791 
792  if(bFlowControlEnable_)
793  {
795  DEBUG_LEVEL,
796  "MACI %03hu TDMA::BaseModel::%s received a flow control token request/response",
797  id_,
798  __func__);
799 
800  flowControlManager_.processFlowControlMessage(pFlowControlControlMessage);
801  }
802  else
803  {
805  ERROR_LEVEL,
806  "MACI %03hu TDMA::BaseModel::%s received a flow control token request but"
807  " flow control is not enabled",
808  id_,
809  __func__);
810  }
811  }
812  break;
813 
815  {
816  const auto pSerializedControlMessage =
817  static_cast<const Controls::SerializedControlMessage *>(pMessage);
818 
819  switch(pSerializedControlMessage->getSerializedId())
820  {
822  {
823  std::unique_ptr<Controls::FlowControlControlMessage>
824  pFlowControlControlMessage{
825  Controls::FlowControlControlMessage::create(pSerializedControlMessage->getSerialization())};
826 
827  if(bFlowControlEnable_)
828  {
829  flowControlManager_.processFlowControlMessage(pFlowControlControlMessage.get());
830  }
831  else
832  {
834  ERROR_LEVEL,
835  "MACI %03hu TDMA::BaseModel::%s received a flow control token request but"
836  " flow control is not enabled",
837  id_,
838  __func__);
839  }
840  }
841  break;
842  }
843  }
844  }
845  }
846 }
847 
848 
850  const ControlMessages &)
851 {
853  DEBUG_LEVEL,
854  "MACI %03hu TDMA::BaseModel::%s",
855  id_,
856  __func__);
857 
858 
859  // check flow control
860  if(bFlowControlEnable_)
861  {
862  auto status = flowControlManager_.removeToken();
863 
864  if(status.second == false)
865  {
867  ERROR_LEVEL,
868  "MACI %03hu TDMA::BaseModel::%s: failed to remove token, drop packet (tokens:%hu)",
869  id_,
870  __func__,
871  status.first);
872 
873  const auto & pktInfo = pkt.getPacketInfo();
874 
875  packetStatusPublisher_.outbound(pktInfo.getSource(),
876  pktInfo.getSource(),
877  pktInfo.getPriority(),
878  pkt.length(),
880 
881  // drop
882  return;
883  }
884  }
885 
886  std::uint8_t u8Queue{priorityToQueue(pkt.getPacketInfo().getPriority())};
887 
888  size_t packetsDropped{pQueueManager_->enqueue(u8Queue,std::move(pkt))};
889 
890  // drop, replace token
891  if(bFlowControlEnable_)
892  {
893  for(size_t i = 0; i < packetsDropped; ++i)
894  {
895  auto status = flowControlManager_.addToken();
896 
897  if(!status.second)
898  {
900  ERROR_LEVEL,
901  "MACI %03hu TDMA::BaseModel:::%s: failed to add token (tokens:%hu)",
902  id_,
903  __func__,
904  status.first);
905  }
906  }
907  }
908 }
909 
910 
912  const Serialization & serialization)
913 {
915  DEBUG_LEVEL,
916  "MACI %03hu TDMA::BaseModel::%s",
917  id_,
918  __func__);
919 
920  pScheduler_->processEvent(eventId,serialization);
921 
922 }
923 
924 
926 {
928  DEBUG_LEVEL,
929  "MACI %03hu TDMA::BaseModel::%s",
930  id_,
931  __func__);
932 
933 
934  ConfigurationUpdate schedulerConfiguration;
935 
936  for(const auto & item : update)
937  {
938  if(item.first == "enablepromiscuousmode")
939  {
940  bool bPromiscuousMode{item.second[0].asBool()};
941 
943  INFO_LEVEL,
944  "MACI %03hu TDMA::BaseModel::%s: %s = %s",
945  id_,
946  __func__,
947  item.first.c_str(),
948  bPromiscuousMode ? "on" : "off");
949 
950  receiveManager_.setPromiscuousMode(bPromiscuousMode);
951  }
952  else
953  {
954  schedulerConfiguration.push_back(item);
955  }
956  }
957 
958  pScheduler_->configure(schedulerConfiguration);
959 }
960 
962  std::uint64_t u64BandwidthHz,
963  const Microseconds & slotDuration,
964  const Microseconds & slotOverhead)
965 {
967  DEBUG_LEVEL,
968  "MACI %03hu TDMA::BaseModel::%s",
969  id_,
970  __func__);
971 
972  // increment index to indicate a schedule change
973  ++u64ScheduleIndex_;
974 
975  if(transmitTimedEventId_)
976  {
977  pPlatformService_->timerService().cancelTimedEvent(transmitTimedEventId_);
978  transmitTimedEventId_ = 0;
979  }
980 
981  if(u64BandwidthHz_ != u64BandwidthHz || frequencies != frequencies_)
982  {
983  // only required if freq set/bandwidth differs from existing
984  pRadioModel_->sendDownstreamControl({Controls::FrequencyOfInterestControlMessage::create(u64BandwidthHz,frequencies)});
985 
986  frequencies_ = frequencies;
987 
988  u64BandwidthHz_ = u64BandwidthHz;
989  }
990 
991  slotDuration_ = slotDuration;
992 
993  slotOverhead_ = slotOverhead;
994 
995  slotStatusTablePublisher_.clear();
996 
997  std::tie(txSlotInfos_,
998  nextMultiFrameTime_) =
999  pScheduler_->getTxSlotInfo(Clock::now(),1);
1000 
1002  DEBUG_LEVEL,
1003  TxSlotInfosFormatter(txSlotInfos_),
1004  "MACI %03hu TDMA::BaseModel::%s TX Slot Info",
1005  id_,
1006  __func__);
1007 
1008 
1009 
1010  if(!txSlotInfos_.empty())
1011  {
1012  pendingTxSlotInfo_ = *txSlotInfos_.begin();
1013 
1014  txSlotInfos_.pop_front();
1015 
1016  transmitTimedEventId_ =
1018  schedule(std::bind(&Implementation::processTxOpportunity,
1019  this,
1020  u64ScheduleIndex_),
1021  pendingTxSlotInfo_.timePoint_);
1022  }
1023 }
1024 
1026 {
1028  DEBUG_LEVEL,
1029  "MACI %03hu TDMA::BaseModel::%s",
1030  id_,
1031  __func__);
1032 
1033  // enqueue into max priority queue
1034  pQueueManager_->enqueue(4,std::move(pkt));
1035 }
1036 
1037 
1039 {
1041  DEBUG_LEVEL,
1042  "MACI %03hu TDMA::BaseModel::%s",
1043  id_,
1044  __func__);
1045 
1046  pRadioModel_->sendDownstreamControl(msgs);
1047 }
1048 
1049 
1051 {
1053  DEBUG_LEVEL,
1054  "MACI %03hu TDMA::BaseModel::%s",
1055  id_,
1056  __func__);
1057 
1058  return pQueueManager_->getPacketQueueInfo();
1059 }
1060 
1061 void EMANE::Models::TDMA::BaseModel::Implementation::sendDownstreamPacket(double dSlotPortionRatio)
1062 {
1063  // calculate the number of bytes allowed in the slot
1064  size_t bytesAvailable =
1065  (slotDuration_.count() - slotOverhead_.count()) / 1000000.0 * pendingTxSlotInfo_.u64DataRatebps_ / 8.0;
1066 
1067  auto entry = pQueueManager_->dequeue(pendingTxSlotInfo_.u8QueueId_,
1068  bytesAvailable,
1069  pendingTxSlotInfo_.destination_);
1070 
1071  MessageComponents & components = std::get<0>(entry);
1072  size_t totalSize{std::get<1>(entry)};
1073 
1074  if(totalSize)
1075  {
1076  if(totalSize <= bytesAvailable)
1077  {
1078  float fSeconds{totalSize * 8.0f / pendingTxSlotInfo_.u64DataRatebps_};
1079 
1080  Microseconds duration{std::chrono::duration_cast<Microseconds>(DoubleSeconds{fSeconds})};
1081 
1082  // rounding error corner case mitigation
1083  if(duration >= slotDuration_)
1084  {
1085  duration = slotDuration_ - Microseconds{1};
1086  }
1087 
1088  NEMId dst{};
1089  size_t completedPackets{};
1090 
1091  // determine how many components represent completed packets (no fragments remain) and
1092  // whether to use a unicast or broadcast nem address
1093  for(const auto & component : components)
1094  {
1095  completedPackets += !component.isMoreFragments();
1096 
1097  // if not set, set a destination
1098  if(!dst)
1099  {
1100  dst = component.getDestination();
1101  }
1102  else if(dst != NEM_BROADCAST_MAC_ADDRESS)
1103  {
1104  // if the destination is not broadcast, check to see if it matches
1105  // the destination of the current component - if not, set the NEM
1106  // broadcast address as the dst
1107  if(dst != component.getDestination())
1108  {
1110  }
1111  }
1112  }
1113 
1114 
1116  DEBUG_LEVEL,
1117  "MACI %03hu TDMA::BaseModel::%s sending downstream to %03hu components: %zu",
1118  id_,
1119  __func__,
1120  dst,
1121  components.size());
1122 
1123 
1124  if(bFlowControlEnable_ && completedPackets)
1125  {
1126  auto status = flowControlManager_.addToken(completedPackets);
1127 
1128  if(!status.second)
1129  {
1131  ERROR_LEVEL,
1132  "MACI %03hu TDMA::BaseModel::%s: failed to add token (tokens:%hu)",
1133  id_,
1134  __func__,
1135  status.first);
1136 
1137  }
1138  }
1139 
1140  aggregationStatusPublisher_.update(components);
1141 
1142  BaseModelMessage baseModelMessage{pendingTxSlotInfo_.u64AbsoluteSlotIndex_,
1143  pendingTxSlotInfo_.u64DataRatebps_,
1144  std::move(components)};
1145 
1146  Serialization serialization{baseModelMessage.serialize()};
1147 
1148  auto now = Clock::now();
1149 
1150  DownstreamPacket pkt({id_,dst,0,now},serialization.c_str(),serialization.size());
1151 
1152  pkt.prependLengthPrefixFraming(serialization.size());
1153 
1154  pRadioModel_->sendDownstreamPacket(CommonMACHeader{REGISTERED_EMANE_MAC_TDMA,u64SequenceNumber_++},
1155  pkt,
1157  u64BandwidthHz_,
1158  {{pendingTxSlotInfo_.u64FrequencyHz_,duration}}),
1161 
1162  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1163  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1164  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1166  dSlotPortionRatio);
1167 
1168  neighborMetricManager_.updateNeighborTxMetric(dst,
1169  pendingTxSlotInfo_.u64DataRatebps_,
1170  now);
1171  }
1172  else
1173  {
1175  ERROR_LEVEL,
1176  "MACI %03hu TDMA::BaseModel::%s queue dequeue returning %zu bytes than slot has available %zu",
1177  id_,
1178  __func__,
1179  totalSize,
1180  bytesAvailable);
1181  }
1182  }
1183  else
1184  {
1185  // nothing to transmit, update the slot table to record how well
1186  // schedule is being serviced
1187  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1188  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1189  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1191  dSlotPortionRatio);
1192  }
1193 }
1194 
1195 void EMANE::Models::TDMA::BaseModel::Implementation::processTxOpportunity(std::uint64_t u64ScheduleIndex)
1196 {
1197  // check for scheduled timer functor after new schedule, if so disregard
1198  if(u64ScheduleIndex != u64ScheduleIndex_)
1199  {
1201  ERROR_LEVEL,
1202  "MACI %03hu TDMA::BaseModel::%s old schedule tx opportunity found"
1203  " scheduled index: %zu current index: %zu",
1204  id_,
1205  __func__,
1206  u64ScheduleIndex,
1207  u64ScheduleIndex_);
1208  return;
1209  }
1210 
1211  auto now = Clock::now();
1212 
1213  auto nowSlotInfo = pScheduler_->getSlotInfo(now);
1214 
1215  if(nowSlotInfo.u64AbsoluteSlotIndex_ == pendingTxSlotInfo_.u64AbsoluteSlotIndex_)
1216  {
1217  // transmit in this slot
1218  sendDownstreamPacket(slotPortionRatio(now,
1219  pendingTxSlotInfo_.timePoint_));
1220  }
1221  else
1222  {
1223  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1224  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1225  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1227  slotPortionRatio(now,
1228  pendingTxSlotInfo_.timePoint_));
1229  }
1230 
1231 
1232  // if necessary request more tx opportunities
1233  if(txSlotInfos_.empty())
1234  {
1235  // request more slots
1236  std::tie(txSlotInfos_,
1237  nextMultiFrameTime_) =
1238  pScheduler_->getTxSlotInfo(nextMultiFrameTime_,1);
1239  }
1240 
1241  bool bFoundTXSlot = {};
1242 
1243  // find the next transmit opportunity
1244  while(!txSlotInfos_.empty() && !bFoundTXSlot)
1245  {
1246  // it might be necessary to request more opportunies if we
1247  // are behind and have many tx opportunities that have past
1248  while(!txSlotInfos_.empty())
1249  {
1250  pendingTxSlotInfo_ = *txSlotInfos_.begin();
1251 
1252  txSlotInfos_.pop_front();
1253 
1254  if(pendingTxSlotInfo_.u64AbsoluteSlotIndex_ > nowSlotInfo.u64AbsoluteSlotIndex_)
1255  {
1256  // need to schedule processing in the future
1257 
1258  transmitTimedEventId_ =
1260  schedule(std::bind(&Implementation::processTxOpportunity,
1261  this,
1262  u64ScheduleIndex_),
1263  pendingTxSlotInfo_.timePoint_);
1264 
1265  bFoundTXSlot = true;
1266  break;
1267  }
1268  else if(pendingTxSlotInfo_.u64AbsoluteSlotIndex_ < nowSlotInfo.u64AbsoluteSlotIndex_)
1269  {
1270  // blown tx opportunity
1271  slotStatusTablePublisher_.update(pendingTxSlotInfo_.u32RelativeIndex_,
1272  pendingTxSlotInfo_.u32RelativeFrameIndex_,
1273  pendingTxSlotInfo_.u32RelativeSlotIndex_,
1275  slotPortionRatio(now,
1276  pendingTxSlotInfo_.timePoint_));
1277  }
1278  else
1279  {
1280  // send the packet
1281  sendDownstreamPacket(slotPortionRatio(now,
1282  pendingTxSlotInfo_.timePoint_));
1283  }
1284  }
1285 
1286  // if we are out of slots
1287  if(txSlotInfos_.empty())
1288  {
1289  // request more slots
1290  std::tie(txSlotInfos_,
1291  nextMultiFrameTime_) =
1292  pScheduler_->getTxSlotInfo(nextMultiFrameTime_,1);
1293  }
1294  }
1295 
1296  return;
1297 }
1298 
1299 double EMANE::Models::TDMA::BaseModel::Implementation::slotPortionRatio(const TimePoint & current,
1300  const TimePoint & slotTime)
1301 {
1302  Microseconds delta = std::chrono::duration_cast<Microseconds>(current -
1303  slotTime);
1304 
1305  return delta.count() / static_cast<double>(slotDuration_.count());
1306 }
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