using System.Collections.Concurrent; namespace Substation; public class State { public readonly object Lock = new(); public bool DirtyA, DirtyB; public int DesiredA, DesiredB; public byte[]? LoopFreqA, LoopIntA; public byte[]? LoopFreqB, LoopIntB; public readonly ConcurrentQueue StreamA = new(); public readonly ConcurrentQueue StreamB = new(); public int ActualA, ActualB; public int LastSentA, LastSentB; public int MaxStrength = 200; public LimitMode LimitMode = LimitMode.Clamp; public void SetLimit(int max, LimitMode mode) { lock (Lock) { MaxStrength = Math.Clamp(max, 0, 200); LimitMode = mode; DirtyA = true; DirtyB = true; } } public bool IsSignaling { get { lock (Lock) { bool hasPattern = LoopFreqA != null || LoopFreqB != null || !StreamA.IsEmpty || !StreamB.IsEmpty; bool hasStrength = DesiredA > 0 || DesiredB > 0; return hasPattern && hasStrength; } } } public void SetStrength(char ch, int value) { lock (Lock) { var clamped = Math.Clamp(value, 0, 200); if (ch == 'A') { DesiredA = clamped; DirtyA = true; } else { DesiredB = clamped; DirtyB = true; } } } public void SetLoop(char ch, byte[] freq, byte[] intensity) { lock (Lock) { if (ch == 'A') { LoopFreqA = freq; LoopIntA = intensity; } else { LoopFreqB = freq; LoopIntB = intensity; } } } public void EnqueueStream(char ch, IEnumerable frames) { var queue = ch == 'A' ? StreamA : StreamB; foreach (var f in frames) queue.Enqueue(f); } public void Stop(char ch) { lock (Lock) { if (ch == 'A') { LoopFreqA = null; LoopIntA = null; } else { LoopFreqB = null; LoopIntB = null; } } var queue = ch == 'A' ? StreamA : StreamB; while (queue.TryDequeue(out _)) { } } public (StrengthMode modeA, byte valA, StrengthMode modeB, byte valB, byte[] freqA, byte[] intA, byte[] freqB, byte[] intB) ConsumeTick() { lock (Lock) { var modeA = DirtyA ? StrengthMode.Abs : StrengthMode.None; var modeB = DirtyB ? StrengthMode.Abs : StrengthMode.None; var limit = MaxStrength; var limMode = LimitMode; byte ApplyLimit(int desired) => limMode == LimitMode.Scale ? (byte)(desired * limit / 200) : (byte)Math.Clamp(desired, 0, limit); var valA = ApplyLimit(DesiredA); var valB = ApplyLimit(DesiredB); LastSentA = valA; LastSentB = valB; DirtyA = false; DirtyB = false; var (freqA, intA) = PopWave(StreamA, LoopFreqA, LoopIntA); var (freqB, intB) = PopWave(StreamB, LoopFreqB, LoopIntB); return (modeA, valA, modeB, valB, freqA, intA, freqB, intB); } } static (byte[] freq, byte[] intensity) PopWave( ConcurrentQueue stream, byte[]? loopFreq, byte[]? loopInt) { if (stream.TryDequeue(out var frame)) return (frame.Freq, frame.Intensity); if (loopFreq != null && loopInt != null) return (loopFreq, loopInt); return (B0.ChannelOff, B0.IntensityOff); } }