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