1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Time; 15 16 namespace Antmicro.Renode.Peripherals.Wireless 17 { 18 public abstract class WirelessMedium : IHasChildren<IMediumFunction>, IExternal, IConnectable<IRadio>, INetworkLogWireless 19 { WirelessMedium()20 protected WirelessMedium() 21 { 22 radioHooks = new Dictionary<IRadio, Action<byte[]>>(); 23 mediumFunction = SimpleMediumFunction.Instance; 24 radios = new Dictionary<IRadio, Position>(); 25 } 26 AttachTo(IRadio radio)27 public void AttachTo(IRadio radio) 28 { 29 if(radios.ContainsKey(radio)) 30 { 31 throw new RecoverableException("Cannot attach to the provided radio as it is already registered in this wireless medium."); 32 } 33 radios.Add(radio, new Position()); 34 radio.FrameSent += FrameSentHandler; 35 } 36 DetachFrom(IRadio radio)37 public void DetachFrom(IRadio radio) 38 { 39 radios.Remove(radio); 40 radioHooks.Remove(radio); 41 radio.FrameSent -= FrameSentHandler; 42 } 43 SetMediumFunction(IMediumFunction function)44 public void SetMediumFunction(IMediumFunction function) 45 { 46 mediumFunction = function; 47 } 48 SetPosition(IRadio radio, decimal x, decimal y, decimal z)49 public void SetPosition(IRadio radio, decimal x, decimal y, decimal z) 50 { 51 if(radios.ContainsKey(radio)) 52 { 53 radios[radio] = new Position(x, y, z); 54 } 55 else 56 { 57 EmulationManager.Instance.CurrentEmulation.TryGetEmulationElementName(radio, out string name); 58 this.Log(LogLevel.Error, $"Cannot set position for {name} as it is not registered in this wireless medium."); 59 } 60 } 61 GetNames()62 public IEnumerable<string> GetNames() 63 { 64 return new[] { mediumFunction.FunctionName }; 65 } 66 GetAttachedRadiosNames()67 public IEnumerable<string> GetAttachedRadiosNames() 68 { 69 var result = new List<string>(); 70 var currentEmulation = EmulationManager.Instance.CurrentEmulation; 71 foreach(var radio in radios) 72 { 73 currentEmulation.TryGetEmulationElementName(radio.Key, out var name); 74 result.Add(name); 75 } 76 return result; 77 } 78 AttachHookToRadio(IRadio radio, Action<byte[]> hook)79 public void AttachHookToRadio(IRadio radio, Action<byte[]> hook) 80 { 81 radioHooks[radio] = hook; 82 } 83 TryGetByName(string name, out bool success)84 public IMediumFunction TryGetByName(string name, out bool success) 85 { 86 if(mediumFunction.FunctionName == name) 87 { 88 success = true; 89 return mediumFunction; 90 } 91 success = false; 92 return null; 93 } 94 95 public event Action<IExternal, IRadio, IRadio, byte[]> FrameTransmitted; 96 public event Action<IExternal, IRadio, byte[]> FrameProcessed; 97 FrameSentHandler(IRadio sender, byte[] packet)98 private void FrameSentHandler(IRadio sender, byte[] packet) 99 { 100 var senderPosition = radios[sender]; 101 var currentEmulation = EmulationManager.Instance.CurrentEmulation; 102 currentEmulation.TryGetEmulationElementName(sender, out var senderName); 103 104 FrameProcessed?.Invoke(this, sender, packet); 105 106 if(!mediumFunction.CanTransmit(senderPosition)) 107 { 108 this.NoisyLog("Packet from {0} can't be transmitted, size {1}.", senderName, packet.Length); 109 return; 110 } 111 112 foreach(var radioAndPosition in radios.Where(x => x.Key != sender)) 113 { 114 var receiver = radioAndPosition.Key; 115 116 currentEmulation.TryGetEmulationElementName(receiver, out var receiverName); 117 if(!mediumFunction.CanReach(senderPosition, radioAndPosition.Value) || receiver.Channel != sender.Channel) 118 { 119 this.NoisyLog("Packet {0}:chan{1} -> {2}:chan{3} NOT delivered, size {4}.", 120 senderName, sender.Channel, receiverName, receiver.Channel, packet.Length); 121 continue; 122 } 123 124 if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts)) 125 { 126 // e.g. when the sender is a SLIP radio 127 vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld); 128 } 129 130 var packetCopy = packet.ToArray(); 131 if(radioHooks.TryGetValue(receiver, out var hook)) 132 { 133 hook(packetCopy); 134 } 135 136 if(receiver is ISlipRadio) 137 { 138 // send immediately 139 receiver.ReceiveFrame(packetCopy, sender); 140 continue; 141 } 142 143 receiver.GetMachine().HandleTimeDomainEvent(receiver.ReceiveFrame, packetCopy, sender, vts, () => 144 { 145 this.NoisyLog("Packet {0}:chan{1} -> {2}:chan{3} delivered, size {4}.", 146 senderName, sender.Channel, receiverName, receiver.Channel, packet.Length); 147 FrameTransmitted?.Invoke(this, sender, receiver, packetCopy); 148 }); 149 } 150 } 151 152 private IMediumFunction mediumFunction; 153 private readonly Dictionary<IRadio, Action<byte[]>> radioHooks; 154 private readonly Dictionary<IRadio, Position> radios; 155 } 156 } 157