1 // 2 // Copyright (c) 2010-2022 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Linq; 10 using System.IO; 11 using System.Threading; 12 using System.Collections; 13 using System.Collections.Generic; 14 using Antmicro.Renode.Core; 15 using Antmicro.Renode.Exceptions; 16 using Antmicro.Renode.Logging; 17 using Antmicro.Renode.Time; 18 using Antmicro.Renode.Utilities; 19 20 namespace Antmicro.Renode.Peripherals.Sensors 21 { 22 public class SensorSamplesFifo<T> where T : SensorSample, new() 23 { SensorSamplesFifo()24 public SensorSamplesFifo() 25 { 26 samplesFifo = new Queue<T>(); 27 } 28 DumpOldestSample()29 public void DumpOldestSample() 30 { 31 lock(samplesFifo) 32 { 33 samplesFifo.TryDequeue<T>(out _); 34 } 35 } 36 FeedSample(T sample)37 public virtual void FeedSample(T sample) 38 { 39 lock(samplesFifo) 40 { 41 samplesFifo.Enqueue(sample); 42 } 43 } 44 FeedSamplesPeriodically(IMachine machine, IPeripheral owner, string name, string delayString, uint frequency, IEnumerable<T> samples)45 public IManagedThread FeedSamplesPeriodically(IMachine machine, IPeripheral owner, string name, string delayString, uint frequency, IEnumerable<T> samples) 46 { 47 if(!TimeInterval.TryParse(delayString, out var delay)) 48 { 49 throw new RecoverableException($"Invalid delay: {delayString}"); 50 } 51 52 return FeedSamplesInner(machine, owner, name, delay, frequency, samples, true); 53 } 54 FeedSamplesFromBinaryFile(IMachine machine, IPeripheral owner, string name, string path, string delayString, Func<List<decimal>, T> sampleConstructor = null, Action onFinish = null)55 public IManagedThread FeedSamplesFromBinaryFile(IMachine machine, IPeripheral owner, string name, string path, string delayString, Func<List<decimal>, T> sampleConstructor = null, Action onFinish = null) 56 { 57 if(sampleConstructor == null) 58 { 59 sampleConstructor = values => 60 { 61 var sample = new T(); 62 sample.Load(values); 63 return sample; 64 }; 65 } 66 67 if(!TimeInterval.TryParse(delayString, out var delay)) 68 { 69 throw new RecoverableException($"Invalid delay: {delayString}"); 70 } 71 TimeInterval? firstPacketStartTime = null; 72 73 var result = new ManagedThreadsContainer(); 74 var packets = SensorSamplesPacket<T>.ParseFile(path, sampleConstructor); 75 foreach(var packet in packets) 76 { 77 firstPacketStartTime = firstPacketStartTime ?? packet.StartTime; 78 var packetDelay = packet.StartTime - firstPacketStartTime.Value + delay; 79 var feeder = FeedSamplesInner(machine, owner, name, packetDelay, packet.Frequency, packet.Samples, false, onFinish); 80 result.Add(feeder); 81 } 82 83 return result; 84 } 85 FeedSamplesFromFile(string path, Func<string[], T> sampleConstructor = null)86 public void FeedSamplesFromFile(string path, Func<string[], T> sampleConstructor = null) 87 { 88 if(sampleConstructor == null) 89 { 90 sampleConstructor = stringArray => 91 { 92 var sample = new T(); 93 return sample.TryLoad(stringArray) ? sample : null; 94 }; 95 } 96 97 lock(samplesFifo) 98 { 99 var samples = ParseSamplesFile(path, sampleConstructor); 100 samplesFifo.EnqueueRange(samples); 101 } 102 } 103 Reset()104 public virtual void Reset() 105 { 106 currentSample = null; 107 samplesFifo.Clear(); 108 } 109 TryDequeueNewSample()110 public virtual bool TryDequeueNewSample() 111 { 112 return samplesFifo.TryDequeue(out currentSample); 113 } 114 115 public virtual T Sample => currentSample ?? DefaultSample; 116 117 public T DefaultSample { get; } = new T(); 118 119 public uint SamplesCount => (uint)samplesFifo.Count; 120 FeedSamplesInner(IMachine machine, IPeripheral owner, string name, TimeInterval startDelay, uint frequency, IEnumerable<T> samples, bool repeatMode = false, Action onFinish = null)121 private IManagedThread FeedSamplesInner(IMachine machine, IPeripheral owner, string name, TimeInterval startDelay, uint frequency, IEnumerable<T> samples, bool repeatMode = false, Action onFinish = null) 122 { 123 var samplesBuffer = new SamplesBuffer(samples, autoRewind: repeatMode); 124 125 owner.Log(LogLevel.Noisy, "{0}: {1} samples will be fed at {2} Hz starting at {3:c}", name, samplesBuffer.TotalSamplesCount, frequency, startDelay.ToTimeSpan()); 126 127 Action feedSample = () => 128 { 129 if(!samplesBuffer.TryGetNextSample(out var sample)) 130 { 131 return; 132 } 133 owner.Log(LogLevel.Noisy, "{0} {1}: Feeding sample {2}/{3}: {4}", machine.ElapsedVirtualTime.TimeElapsed, name, samplesBuffer.CurrentSampleIndex, samplesBuffer.TotalSamplesCount, sample); 134 FeedSample(sample); 135 }; 136 137 Func<bool> stopCondition = () => 138 { 139 if(samplesBuffer.IsFinished) 140 { 141 owner.Log(LogLevel.Noisy, "{0} {1}: All samples fed", machine.ElapsedVirtualTime.TimeElapsed, name); 142 onFinish?.Invoke(); 143 return true; 144 } 145 return false; 146 }; 147 148 var feederThread = machine.ObtainManagedThread(feedSample, frequency, name + " sample loader", owner, stopCondition); 149 feederThread.StartDelayed(startDelay); 150 return feederThread; 151 } 152 ParseSamplesFile(string path, Func<string[], T> sampleConstructor)153 private IEnumerable<T> ParseSamplesFile(string path, Func<string[], T> sampleConstructor) 154 { 155 var localQueue = new Queue<T>(); 156 var lineNumber = 0; 157 158 try 159 { 160 using(var reader = File.OpenText(path)) 161 { 162 var line = ""; 163 while((line = reader.ReadLine()) != null) 164 { 165 ++lineNumber; 166 167 if(line.Trim().StartsWith("#")) 168 { 169 // this is a comment, just ignore 170 continue; 171 } 172 173 var numbers = line.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim()).ToArray(); 174 175 var sample = sampleConstructor(numbers); 176 if(sample == null) 177 { 178 throw new RecoverableException($"Wrong data file format at line {lineNumber}: {line}"); 179 } 180 localQueue.Enqueue(sample); 181 } 182 } 183 } 184 catch(Exception e) 185 { 186 if(e is RecoverableException) 187 { 188 throw; 189 } 190 191 throw new RecoverableException($"There was a problem when reading samples file: {e.Message}"); 192 } 193 194 return localQueue; 195 } 196 197 private T currentSample; 198 private readonly Queue<T> samplesFifo; 199 200 private class ManagedThreadsContainer : IManagedThread 201 { Add(IManagedThread t)202 public void Add(IManagedThread t) 203 { 204 threads.Add(t); 205 } 206 Dispose()207 public void Dispose() 208 { 209 foreach(var t in threads) 210 { 211 t.Dispose(); 212 } 213 } 214 Start()215 public void Start() 216 { 217 foreach(var t in threads) 218 { 219 t.Start(); 220 } 221 } 222 StartDelayed(TimeInterval i)223 public void StartDelayed(TimeInterval i) 224 { 225 foreach(var t in threads) 226 { 227 t.StartDelayed(i); 228 } 229 } 230 Stop()231 public void Stop() 232 { 233 foreach(var t in threads) 234 { 235 t.Stop(); 236 } 237 } 238 239 public uint Frequency 240 { 241 // We assume, that all threads have the same frequency 242 get => threads[0].Frequency; 243 set 244 { 245 foreach(var t in threads) 246 { 247 t.Frequency = value; 248 } 249 } 250 } 251 252 private readonly List<IManagedThread> threads = new List<IManagedThread>(); 253 } 254 255 private class SamplesBuffer 256 { SamplesBuffer(IEnumerable<T> samples, bool autoRewind)257 public SamplesBuffer(IEnumerable<T> samples, bool autoRewind) 258 { 259 internalSamples = new List<T>(samples); 260 this.autoRewind = autoRewind; 261 } 262 TryGetNextSample(out T sample)263 public bool TryGetNextSample(out T sample) 264 { 265 if(IsFinished) 266 { 267 sample = default(T); 268 return false; 269 } 270 271 // if `autoRewind` was false, IsFinished would return `false` 272 if(CurrentSampleIndex == internalSamples.Count) 273 { 274 CurrentSampleIndex = 0; 275 } 276 277 sample = internalSamples[CurrentSampleIndex]; 278 CurrentSampleIndex++; 279 return true; 280 } 281 282 public bool IsFinished => internalSamples.Count == 0 || (CurrentSampleIndex == internalSamples.Count && !autoRewind); 283 284 public int TotalSamplesCount => internalSamples.Count; 285 286 public int CurrentSampleIndex { get; private set; } 287 288 private readonly bool autoRewind; 289 private readonly List<T> internalSamples; 290 } 291 } 292 } 293