private void FrameSentHandler(IRadio sender, byte[] packet)
        {
            var senderPosition   = radios[sender];
            var currentEmulation = EmulationManager.Instance.CurrentEmulation;

            currentEmulation.TryGetEmulationElementName(sender, out var senderName);

            FrameProcessed?.Invoke(this, sender, packet);

            if (!mediumFunction.CanTransmit(senderPosition))
            {
                this.NoisyLog("Packet from {0} can't be transmitted, size {1}.", senderName, packet.Length);
                return;
            }

            foreach (var radioAndPosition in radios.Where(x => x.Key != sender))
            {
                var receiver = radioAndPosition.Key;

                currentEmulation.TryGetEmulationElementName(receiver, out var receiverName);
                if (!mediumFunction.CanReach(senderPosition, radioAndPosition.Value) || receiver.Channel != sender.Channel)
                {
                    this.NoisyLog("Packet {0} -> {1} NOT delivered, size {2}.", senderName, receiverName, packet.Length);
                    return;
                }

                receiver.GetMachine().HandleTimeDomainEvent(receiver.ReceiveFrame, packet.ToArray(), TimeDomainsManager.Instance.VirtualTimeStamp, () =>
                {
                    this.NoisyLog("Packet {0} -> {1} delivered, size {2}.", senderName, receiverName, packet.Length);
                    FrameTransmitted?.Invoke(this, sender, receiver, packet);
                });
            }
        }
        private void ForwardToReceiver(EthernetFrame frame, IMACInterface sender)
        {
            this.Log(LogLevel.Noisy, "Received frame from interface {0}", sender.MAC);

            if (!frame.DestinationMAC.HasValue)
            {
                this.Log(LogLevel.Warning, "Destination MAC not set, the frame has unsupported format.");
                return;
            }

            FrameProcessed?.Invoke(this, sender, frame.Bytes);

            if (!started)
            {
                return;
            }
            lock (innerLock)
            {
                var interestingIfaces = macMapping.TryGetValue(frame.DestinationMAC.Value, out var destIface)
                    ? ifaces.Where(x => (x.PromiscuousMode && x.Interface != sender) || x.Interface == destIface)
                    : ifaces.Where(x => x.Interface != sender);

                if (!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts))
                {
                    // it happens when sending from tap interface
                    vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld);
                }

                foreach (var iface in interestingIfaces)
                {
                    this.Log(LogLevel.Noisy, "Forwarding frame to interface {0}", iface.Interface.MAC);

                    if (iface.AsTap != null)
                    {
                        iface.AsTap.ReceiveFrame(frame.Clone());
                        continue;
                    }

                    iface.Machine.HandleTimeDomainEvent(iface.Interface.ReceiveFrame, frame.Clone(), vts, () =>
                    {
                        FrameTransmitted?.Invoke(this, sender, iface.Interface, frame.Bytes);
                    });
                }
            }

            // at the same we will potentially add current MAC address assigned to the source
            if (!frame.SourceMAC.HasValue)
            {
                this.Log(LogLevel.Warning, "Source MAC not set, cannot update switch cache.");
                return;
            }

            lock (innerLock)
            {
                macMapping[frame.SourceMAC.Value] = sender;
            }
        }
        private void FrameSentHandler(IRadio sender, byte[] packet)
        {
            var senderPosition   = radios[sender];
            var currentEmulation = EmulationManager.Instance.CurrentEmulation;

            currentEmulation.TryGetEmulationElementName(sender, out var senderName);

            FrameProcessed?.Invoke(this, sender, packet);

            if (!mediumFunction.CanTransmit(senderPosition))
            {
                this.NoisyLog("Packet from {0} can't be transmitted, size {1}.", senderName, packet.Length);
                return;
            }

            foreach (var radioAndPosition in radios.Where(x => x.Key != sender))
            {
                var receiver = radioAndPosition.Key;

                currentEmulation.TryGetEmulationElementName(receiver, out var receiverName);
                if (!mediumFunction.CanReach(senderPosition, radioAndPosition.Value) || receiver.Channel != sender.Channel)
                {
                    this.NoisyLog("Packet {0}:chan{1} -> {2}:chan{3} NOT delivered, size {4}.",
                                  senderName, sender.Channel, receiverName, receiver.Channel, packet.Length);
                    continue;
                }

                if (!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts))
                {
                    // e.g. when the sender is a SLIP radio
                    vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld);
                }

                var packetCopy = packet.ToArray();
                if (radioHooks.TryGetValue(receiver, out var hook))
                {
                    hook(packetCopy);
                }

                if (receiver is ISlipRadio)
                {
                    // send immediately
                    receiver.ReceiveFrame(packetCopy);
                    continue;
                }

                receiver.GetMachine().HandleTimeDomainEvent(receiver.ReceiveFrame, packetCopy, vts, () =>
                {
                    this.NoisyLog("Packet {0}:chan{1} -> {2}:chan{3} delivered, size {4}.",
                                  senderName, sender.Channel, receiverName, receiver.Channel, packet.Length);
                    FrameTransmitted?.Invoke(this, sender, receiver, packetCopy);
                });
            }
        }