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