1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Utilities; 11 using System.Linq; 12 using Antmicro.Migrant; 13 using Antmicro.Migrant.Hooks; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Utilities.Collections; 16 using Antmicro.Renode.UserInterface; 17 using Antmicro.Renode.Core; 18 19 namespace Antmicro.Renode.Peripherals 20 { 21 public class BackendManager : IDisposable 22 { BackendManager()23 public BackendManager() 24 { 25 map = new SerializableWeakKeyDictionary<IAnalyzable, IAnalyzableBackend>(); 26 Init(); 27 } 28 Dispose()29 public void Dispose() 30 { 31 foreach(var analyzer in activeAnalyzers.OfType<IDisposable>()) 32 { 33 analyzer.Dispose(); 34 } 35 TypeManager.Instance.AutoLoadedType -= HandleAutoLoadTypeFound; 36 } 37 GetAvailableAnalyzersFor(IAnalyzableBackend backend)38 public IEnumerable<string> GetAvailableAnalyzersFor(IAnalyzableBackend backend) 39 { 40 if (!analyzers.ContainsKey(backend.GetType())) 41 { 42 return new string[0]; 43 } 44 45 return analyzers[backend.GetType()].Where(x => x.Item2).Select(x => x.Item1.FullName); 46 } 47 SetPreferredAnalyzer(Type backendType, Type analyzerType)48 public void SetPreferredAnalyzer(Type backendType, Type analyzerType) 49 { 50 preferredAnalyzer[backendType] = analyzerType; 51 } 52 GetPreferredAnalyzerFor(IAnalyzableBackend backend)53 public string GetPreferredAnalyzerFor(IAnalyzableBackend backend) 54 { 55 return preferredAnalyzer.ContainsKey(backend.GetType()) ? ((IAnalyzableBackendAnalyzer)Activator.CreateInstance(preferredAnalyzer[backend.GetType()])).GetType().FullName : null; 56 } 57 58 public bool TryCreateBackend<T>(T analyzable) where T : IAnalyzable 59 { 60 Type backendType = null; 61 foreach(var b in backends) 62 { 63 if(b.Key.IsAssignableFrom(analyzable.GetType())) 64 { 65 backendType = b.Value; 66 break; 67 } 68 } 69 70 if(backendType != null) 71 { 72 dynamic backend = (IAnalyzableBackend) Activator.CreateInstance(backendType); 73 backend.Attach((dynamic)analyzable); 74 map[analyzable] = backend; 75 76 return true; 77 } 78 return false; 79 } 80 TryGetBackendFor(IAnalyzable peripheral, out IAnalyzableBackend backend)81 public bool TryGetBackendFor(IAnalyzable peripheral, out IAnalyzableBackend backend) 82 { 83 return map.TryGetValue(peripheral, out backend); 84 } 85 86 public bool TryGetBackendFor<T>(T element, out IAnalyzableBackend<T> backend) where T : IAnalyzable 87 { 88 IAnalyzableBackend outValue = null; 89 var result = map.TryGetValue(element, out outValue); 90 backend = (IAnalyzableBackend<T>)outValue; 91 return result; 92 } 93 94 public bool TryCreateAnalyzerForBackend<T>(T backend, out IAnalyzableBackendAnalyzer analyzer) where T : IAnalyzableBackend 95 { 96 Type analyzerType; 97 var backendType = backend.GetType(); 98 if(preferredAnalyzer.ContainsKey(backendType)) 99 { 100 analyzerType = preferredAnalyzer[backendType]; 101 } 102 else 103 { 104 List<Tuple<Type, bool>> foundAnalyzers; 105 if(!analyzers.TryGetValue(backendType, out foundAnalyzers) || foundAnalyzers.Count(x => x.Item2) > 1) 106 { 107 analyzer = null; 108 return false; 109 } 110 111 analyzerType = foundAnalyzers.First(x => x.Item2).Item1; 112 } 113 114 analyzer = CreateAndAttach(analyzerType, backend); 115 activeAnalyzers.Add(analyzer); 116 PeripheralBackendAnalyzerCreated?.Invoke(analyzer); 117 return true; 118 } 119 120 public bool TryCreateAnalyzerForBackend<T>(T backend, string analyzerTypeName, out IAnalyzableBackendAnalyzer analyzer) where T : IAnalyzableBackend 121 { 122 if (!analyzers.ContainsKey(backend.GetType())) 123 { 124 analyzer = null; 125 return false; 126 } 127 128 var foundAnalyzers = analyzers[backend.GetType()]; 129 var analyzerType = foundAnalyzers.FirstOrDefault(x => x.Item1.FullName == analyzerTypeName).Item1; 130 if(analyzerType != null) 131 { 132 analyzer = CreateAndAttach(analyzerType, backend); 133 activeAnalyzers.Add(analyzer); 134 PeripheralBackendAnalyzerCreated?.Invoke(analyzer); 135 return true; 136 } 137 138 analyzer = null; 139 return false; 140 } 141 HideAnalyzersFor(IPeripheral peripheral)142 public void HideAnalyzersFor(IPeripheral peripheral) 143 { 144 var toRemove = new List<IAnalyzableBackendAnalyzer>(); 145 foreach(var analyzer in activeAnalyzers.Where(x => x.Backend.AnalyzableElement == peripheral)) 146 { 147 analyzer.Hide(); 148 toRemove.Add(analyzer); 149 } 150 151 foreach(var rem in toRemove) 152 { 153 activeAnalyzers.Remove(rem); 154 } 155 } 156 HideAnalyzersFor(IMachine machine)157 public void HideAnalyzersFor(IMachine machine) 158 { 159 string name; 160 var toRemove = new List<IAnalyzableBackendAnalyzer>(); 161 foreach(var analyzer in activeAnalyzers.Where(x => machine.TryGetLocalName(x.Backend.AnalyzableElement as IPeripheral, out name))) 162 { 163 analyzer.Hide(); 164 toRemove.Add(analyzer); 165 } 166 167 foreach(var rem in toRemove) 168 { 169 activeAnalyzers.Remove(rem); 170 } 171 } 172 173 [field: Transient] 174 public event Action<IAnalyzableBackendAnalyzer> PeripheralBackendAnalyzerCreated; 175 CreateAndAttach(Type analyzerType, object backend)176 private IAnalyzableBackendAnalyzer CreateAndAttach(Type analyzerType, object backend) 177 { 178 dynamic danalyzer = Activator.CreateInstance(analyzerType); 179 danalyzer.AttachTo((dynamic)backend); 180 return (IAnalyzableBackendAnalyzer) danalyzer; 181 } 182 TryCreateAndAttach(Type analyzerType, object backend, Func<IAnalyzableBackendAnalyzer, bool> condition, out IAnalyzableBackendAnalyzer analyzer)183 private bool TryCreateAndAttach(Type analyzerType, object backend, Func<IAnalyzableBackendAnalyzer, bool> condition, out IAnalyzableBackendAnalyzer analyzer) 184 { 185 dynamic danalyzer = Activator.CreateInstance(analyzerType); 186 if(condition(danalyzer)) 187 { 188 danalyzer.AttachTo((dynamic)backend); 189 analyzer = (IAnalyzableBackendAnalyzer)danalyzer; 190 return true; 191 } 192 193 analyzer = null; 194 return false; 195 } 196 HandleAutoLoadTypeFound(Type t)197 private void HandleAutoLoadTypeFound(Type t) 198 { 199 var interestingInterfaces = t.GetInterfaces().Where(i => i.IsGenericType && 200 (i.GetGenericTypeDefinition() == typeof(IAnalyzableBackendAnalyzer<>) || 201 i.GetGenericTypeDefinition() == typeof(IAnalyzableBackend<>))); 202 203 if(!interestingInterfaces.Any()) 204 { 205 return; 206 } 207 208 var hidden = t.GetCustomAttributes(typeof(HideInMonitorAttribute), true).Any(); 209 var analyzerTypes = interestingInterfaces.Where(i => i.GetGenericTypeDefinition() == typeof(IAnalyzableBackendAnalyzer<>)).SelectMany(i => i.GetGenericArguments()).ToArray(); 210 foreach(var arg in analyzerTypes) 211 { 212 if(!analyzers.ContainsKey(arg)) 213 { 214 analyzers.Add(arg, new List<Tuple<Type, bool>>()); 215 } 216 217 analyzers[arg].Add(Tuple.Create(t, !hidden)); 218 } 219 220 var backendTypes = interestingInterfaces.Where(i => i.GetGenericTypeDefinition() == typeof(IAnalyzableBackend<>)).SelectMany(i => i.GetGenericArguments()).ToArray(); 221 foreach(var arg in backendTypes) 222 { 223 if(backends.ContainsKey(arg)) 224 { 225 throw new InvalidProgramException(string.Format("There can be only one backend class for a peripheral type, but found at least two: {0}, {1}", backends[arg].AssemblyQualifiedName, t.AssemblyQualifiedName)); 226 } 227 backends[arg] = t; 228 } 229 } 230 231 [PreSerialization] SavePreferredAnalyzers()232 private void SavePreferredAnalyzers() 233 { 234 preferredAnalyzersString = new Dictionary<string, string>(); 235 foreach(var pa in preferredAnalyzer) 236 { 237 preferredAnalyzersString.Add(pa.Key.AssemblyQualifiedName, pa.Value.AssemblyQualifiedName); 238 } 239 } 240 RestorePreferredAnalyzers()241 private void RestorePreferredAnalyzers() 242 { 243 if(preferredAnalyzersString == null) 244 { 245 return; 246 } 247 248 foreach(var pas in preferredAnalyzersString) 249 { 250 try 251 { 252 preferredAnalyzer.Add(Type.GetType(pas.Key), Type.GetType(pas.Value)); 253 } 254 catch (Exception) 255 { 256 Logger.LogAs(this, LogLevel.Warning, "Could not restore preferred analyzer for {0}: {1}. Error while loading types", pas.Key, pas.Value); 257 } 258 } 259 260 preferredAnalyzersString = null; 261 } 262 263 [PostDeserialization] Init()264 private void Init() 265 { 266 analyzers = new Dictionary<Type, List<Tuple<Type, bool>>>(); 267 backends = new Dictionary<Type, Type>(); 268 preferredAnalyzer = new Dictionary<Type, Type>(); 269 activeAnalyzers = new List<IAnalyzableBackendAnalyzer>(); 270 271 RestorePreferredAnalyzers(); 272 TypeManager.Instance.AutoLoadedType += HandleAutoLoadTypeFound; 273 } 274 275 private SerializableWeakKeyDictionary<IAnalyzable, IAnalyzableBackend> map; 276 [Transient] 277 private Dictionary<Type, List<Tuple<Type, bool>>> analyzers; 278 [Transient] 279 private Dictionary<Type, Type> backends; 280 [Transient] 281 private Dictionary<Type, Type> preferredAnalyzer; 282 283 private Dictionary<string, string> preferredAnalyzersString; 284 285 [Transient] 286 private List<IAnalyzableBackendAnalyzer> activeAnalyzers; 287 } 288 } 289 290