47 const char * pzLayerName =
"CommEffect::Shim";
49 const std::uint32_t TIMED_EVENT_UPSTREAM_PACKET{1};
52 struct TimedEventArg {
53 std::uint32_t u32EventType_;
61 timePoint_{timePoint},
67 const std::uint16_t DROP_CODE_DISCARD = 1;
68 const std::uint16_t DROP_CODE_GROUP_ID = 2;
69 const std::uint16_t DROP_CODE_REGISTRATION_ID = 3;
70 const std::uint16_t DROP_CODE_DST_MAC = 4;
71 const std::uint16_t DROP_CODE_BAD_MSG = 5;
72 const std::uint16_t DROP_CODE_NO_PROFILE_DATA = 6;
73 const std::uint16_t DROP_CODE_RX_BUFF = 7;
74 const std::uint16_t DROP_CODE_TIMER_ERROR = 8;
91 u32UpstreamSequenceNumber_{},
92 u32DownstreamSequenceNumber_{},
93 bDefaultConnectivity_{
true},
94 bEnablePromiscousMode_{},
95 bEnableTightTimingMode_{},
97 receiveBufferPeriod_{},
98 RNDZeroToOneHundred_{0.0f, 100.0f},
99 RNG_(std::chrono::system_clock::now().time_since_epoch().count()),
100 commonLayerStatistics_{STATISTIC_TABLE_LABELS,{},
"0"}
111 "SHIMI %03hu %s::%s",
121 "Defines the default connectivity mode for Comm Effects. When" 122 " set to on, full connectivity will be engaged until a valid" 123 " Comm Effect event is received.");
125 configRegistrar.registerNonNumeric<std::string>(
"filterfile",
128 "Defines the absolute URI of the effects filter XML" 129 " file which contains static filters to control" 130 " network impairments.");
133 configRegistrar.registerNumeric<std::uint32_t>(
"groupid",
136 "Defines the Comm Effect Group Id. Only NEMs in the" 137 " same Comm Effect Group can communicate.");
139 configRegistrar.registerNumeric<
bool>(
"enablepromiscuousmode",
142 "Defines whether promiscuous mode is enabled or not. If promiscuous" 143 " mode is enabled, all received packets (intended for the given node" 144 " or not) that pass the receive test are sent upstream to the transport.");
147 configRegistrar.registerNumeric<
double>(
"receivebufferperiod",
150 "Defines the max buffering time in seconds for packets received from an NEM." 151 " The buffering interval for a given packet is determined by the bitrate" 152 " for the source NEM and the packet size. Packets are then placed in a" 153 " timed queue based on this interval and all packets that would cause the" 154 " receive buffer period to be exceeded are discarded. A value of 0.0" 155 " disables the limit and allows all received packets to stack up in the" 172 "SHIMI %03hu %s::%s",
177 for(
const auto & item : update)
179 if(item.first ==
"defaultconnectivitymode")
181 bDefaultConnectivity_ = item.second[0].asBool();
185 "SHIMI %03hu %s::%s: %s = %d",
190 bDefaultConnectivity_);
192 else if(item.first ==
"enablepromiscuousmode")
194 bEnablePromiscousMode_ = item.second[0].asBool();
198 "SHIMI %03hu %s::%s: %s = %d",
203 bEnablePromiscousMode_);
205 else if(item.first ==
"groupid")
207 u32GroupId_ = item.second[0].asUINT32();
211 "SHIMI %03hu %s::%s: %s = %u",
218 else if(item.first ==
"filterfile")
220 sFilterFile_ = item.second[0].asString();
225 "SHIMI %03hu %s::%s: %s = %s",
230 sFilterFile_.c_str());
232 else if(item.first ==
"receivebufferperiod")
234 DoubleSeconds receiveBufferPeriodSeconds{item.second[0].asDouble()};
236 receiveBufferPeriod_ =
237 std::chrono::duration_cast<
Microseconds>(receiveBufferPeriodSeconds);
241 "SHIMI %03hu %s::%s: %s = %lf",
246 receiveBufferPeriodSeconds.count());
256 "SHIMI %03hu %s::%s",
261 if(!sFilterFile_.empty() && profileManager_.
load(sFilterFile_.c_str()) < 0)
263 throw makeException<StartException>(
"%s : Could not open filter file %s",
265 sFilterFile_.c_str());
274 "SHIMI %03hu %s::%s",
289 "SHIMI %03hu %s::%s",
300 "SHIMI %03hu %s::%s, unexpected control message, drop",
311 "SHIMI %03hu %s::%s, unexpected control message, drop",
333 "SHIMI %03hu %s::%s, ignore phy type %hu != %hu, from %hu to %hu",
343 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
344 DROP_CODE_REGISTRATION_ID);
350 std::uint32_t u32GroupId{};
356 if(len && pkt.
length() >= len)
362 txTime = header.getTxTimePoint();
369 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
376 if(!bEnablePromiscousMode_)
383 "SHIMI %03hu %s::%s, ignore transmission from %hu to %hu",
391 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
400 if(u32GroupId != u32GroupId_)
404 "SHIMI %03hu %s::%s, group id mismatch src %hu, src grp %u != our grp %u",
413 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
427 if(bDefaultConnectivity_)
431 "SHIMI %03hu %s::%s, default connectivity mode, src %hu",
438 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime));
450 "SHIMI %03hu %s::%s, drop, no profile data for src %hu, " 451 "default connectivity is off",
458 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
459 DROP_CODE_NO_PROFILE_DATA);
469 ret.first.getBroadcastBitRate() :
470 ret.first.getUnicastBitRate()};
476 0.0 : (pkt.
length() * 8) / static_cast<double>(u64BitRate)})};
479 auto optionalEORTime = getEORTime(pktInfo.
getSource());
482 if(receiveBufferPeriod_ != Microseconds::zero() && optionalEORTime.second)
485 if((optionalEORTime.first - beginTime) > receiveBufferPeriod_)
489 "SHIMI %03hu %s::%s, drop src %hu, receive buffer period exceeded %lf",
494 std::chrono::duration_cast<
DoubleSeconds>(receiveBufferPeriod_).count());
498 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
506 if(optionalEORTime.first > beginTime)
509 optionalEORTime.first += rxIntervalMicroseconds;
514 optionalEORTime.first = beginTime + rxIntervalMicroseconds;
518 setEORTime(pktInfo.
getSource(),optionalEORTime.first);
522 "SHIMI %03hu %s::%s, src %hu, dst %hu, len %zd, latency %lf," 523 " jitter %lf, loss %f, dups %f, bitrate %ju, rxtime %lf",
524 id_, pzLayerName, __func__,
528 std::chrono::duration_cast<
DoubleSeconds>(ret.first.getLatency()).count(),
529 std::chrono::duration_cast<
DoubleSeconds>(ret.first.getJitter()).count(),
530 ret.first.getProbabilityLoss(),
531 ret.first.getProbabilityDuplicate(),
533 std::chrono::duration_cast<
DoubleSeconds>(rxIntervalMicroseconds).count());
537 const size_t taskCount = getTaskCount(ret.first.getProbabilityLoss(),
538 ret.first.getProbabilityDuplicate());
543 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime),
548 for(
size_t n = 0; n < taskCount; ++n)
551 TimePoint tpTimeout{ret.first.getLatency() + randomize(ret.first.getJitter()) + optionalEORTime.first};
554 if(bEnableTightTimingMode_)
556 tpTimeout -= beginTime - txTime;
561 TimedEventArg * pTimedEventArg{
new TimedEventArg{TIMED_EVENT_UPSTREAM_PACKET,
564 std::chrono::duration_cast<
Microseconds>(tpTimeout - Clock::now())}};
572 "SHIMI %03hu %s::%s, enqueue task %zu, seq %u, delay %lf",
577 u32UpstreamSequenceNumber_,
578 std::chrono::duration_cast<
DoubleSeconds>(tpTimeout - Clock::now()).count());
594 "SHIMI %03hu %s::%s, src %hu, dst %hu, seq %u",
600 u32DownstreamSequenceNumber_);
603 ShimHeader shimHeader{Clock::now(), u32GroupId_, u32DownstreamSequenceNumber_};
608 pkt.
prepend(serialization.c_str(),serialization.size());
613 static_cast<std::uint16_t
>(u32DownstreamSequenceNumber_),
622 commonPHYHeader.prependTo(pkt);
625 std::chrono::duration_cast<Microseconds>(Clock::now() - beginTime));
631 ++u32DownstreamSequenceNumber_;
645 "SHIMI %03hu %s::%s,event id %03hu",
655 bDefaultConnectivity_ =
false;
670 const TimedEventArg * pTimedEventArg {
reinterpret_cast<const TimedEventArg *
>(arg)};
672 #ifdef VERY_VERBOSE_LOGGING 675 "SHIMI %03hu %s::%s: eventId %ld, latency %lf",
680 std::chrono::duration_cast<
DoubleSeconds>(latencyMicroseconds).count());
683 if(pTimedEventArg->u32EventType_ == TIMED_EVENT_UPSTREAM_PACKET)
690 *pPacket, std::chrono::duration_cast<Microseconds>(
691 Clock::now() - pTimedEventArg->timePoint_) - pTimedEventArg->delay_ - latencyMicroseconds);
697 "SHIMI %03hu %s::%s, latency %lf",
701 std::chrono::duration_cast<
DoubleSeconds>(latencyMicroseconds).count());
707 delete pTimedEventArg;
712 size_t EMANE::Models::CommEffect::Shim::getTaskCount(
float fLoss,
float fDups)
721 if((RNDZeroToOneHundred_()) >= fLoss)
743 count += fDups / 100.0f;
746 fDups = fmodf(fDups,100.0f);
749 if((fDups > 0.0f) && (fDups < 100.0f))
752 if((RNDZeroToOneHundred_()) <= fDups)
765 EMANE::Models::CommEffect::Shim::randomize(
const Microseconds & duration)
769 if(duration != Microseconds::zero())
772 randomized =
Microseconds{RNG_() % (2 * duration.count())};
775 randomized -= duration;
782 std::pair<EMANE::TimePoint,bool> EMANE::Models::CommEffect::Shim::getEORTime(
NEMId src)
785 auto iter = EORTimeMap_.find(src);
787 if(iter != EORTimeMap_.end())
790 return std::make_pair(iter->second,
true);
799 void EMANE::Models::CommEffect::Shim::setEORTime(
NEMId src,
const TimePoint & timePoint)
802 auto iter = EORTimeMap_.find(src);
804 if(iter != EORTimeMap_.end())
807 iter->second = timePoint;
812 EORTimeMap_.insert(std::make_pair(src,timePoint));
std::string Serialization
A Packet class that allows upstream processing to strip layer headers as the packet travels up the st...
const PacketInfo & getPacketInfo() const
void processUpstreamControl(const ControlMessages &msgs) override
The Registrar interface provides access to all of the emulator registrars.
DECLARE_SHIM_LAYER(EMANE::Models::CommEffect::Shim)
virtual ConfigurationRegistrar & configurationRegistrar()=0
virtual TimerEventId scheduleTimedEvent(const TimePoint &timePoint, const void *arg=nullptr, const Duration &interval=Duration::zero())=0
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
virtual StatisticRegistrar & statisticRegistrar()=0
void processOutbound(const UpstreamPacket &pkt, Microseconds delay, size_t dropCode={})
std::pair< Events::CommEffect, bool > getProfileData(const void *buf, size_t len, NEMId id) const
Shim(NEMId id, PlatformServiceProvider *pPlatformService, RadioServiceProvider *pRadioService)
std::list< const ControlMessage * > ControlMessages
void processDownstreamControl(const ControlMessages &msgs) override
NEMId getDestination() const
std::uint16_t stripLengthPrefixFraming()
constexpr NEMId NEM_BROADCAST_MAC_ADDRESS
size_t strip(size_t size)
void prepend(const void *buf, size_t size)
virtual EventRegistrar & eventRegistrar()=0
void registerStatistics(StatisticRegistrar &statisticRegistrar)
Store source, destination, creation time and priority information for a packet.
EMANE::NetworkAdapterException __attribute__
Interface used to create a Shim layer plugin implementation.
std::vector< std::string > StatisticTableLabels
const PacketInfo & getPacketInfo() const
void processEvent(const EventId &, const Serialization &) override
void processDownstreamPacket(DownstreamPacket &pkt, const ControlMessages &msgs) override
Specialized packet the allows downstream processing to add layer specific headers as the packet trave...
Comm Effect events are used to set asynchronous link characteristics from one or more transmitting NE...
const RegistrationId REGISTERED_EMANE_PHY_COMM_EFFECT
std::chrono::microseconds Microseconds
std::chrono::duration< double > DoubleSeconds
The RadioServiceProvider interface provides access to radio (RF) model specific services.
std::list< FrequencySegment > FrequencySegments
void initialize(Registrar ®istrar) override
const CommEffects & getCommEffects() const
std::vector< ConfigurationNameAnyValues > ConfigurationUpdate
void processTimedEvent(TimerEventId eventId, const TimePoint &expireTime, const TimePoint &scheduleTime, const TimePoint &fireTime, const void *arg) override
virtual void registerEvent(EventId eventId)=0
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={})
Clock::time_point TimePoint
std::list< Transmitter > Transmitters
void sendUpstreamPacket(UpstreamPacket &pkt, const ControlMessages &msgs=empty)
void prependLengthPrefixFraming(std::uint16_t u16Length)
#define LOGGER_STANDARD_LOGGING(logger, level, fmt, args...)
void sendDownstreamPacket(DownstreamPacket &pkt, const ControlMessages &msgs=empty)
void configure(const ConfigurationUpdate &update) override
void processUpstreamPacket(UpstreamPacket &pkt, const ControlMessages &msgs) override
void processInbound(const UpstreamPacket &pkt)