using System.Diagnostics; using System.Runtime.InteropServices; namespace RobotNet10.RobotApp.Services.Robot; internal static partial class Windows { [LibraryImport("winmm.dll")] internal static partial uint timeBeginPeriod(uint uPeriod); [LibraryImport("winmm.dll")] internal static partial uint timeEndPeriod(uint uPeriod); } public static class HighPrecisionTimerHelper { public static void EnableHighPrecision() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _ = Windows.timeBeginPeriod(2); } } public static void DisableHighPrecision() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _ = Windows.timeEndPeriod(2); } } } public class HighPrecisionTimer(int Interval, Action Callback, Logger? Logger) : IDisposable where T : class { public bool Disposed; private Thread? Thread; private long IntervalTicks; private long NextDueTime; private readonly Lock Lock = new(); private void Handler() { while (!Disposed) { long now = Stopwatch.GetTimestamp(); bool shouldRun = false; lock (Lock) { if (Disposed) break; if (now >= NextDueTime) { shouldRun = true; long scheduledTime = NextDueTime; NextDueTime += IntervalTicks; // Tự đồng bộ nếu lệch quá long driftTicks = now - scheduledTime; if (driftTicks > IntervalTicks / 2) { Logger?.Warning($"High-res timer drift: {driftTicks * 1000.0 / Stopwatch.Frequency:F3}ms. Resync."); NextDueTime = now + IntervalTicks; } } } // === BƯỚC 2: Chạy callback === if (shouldRun) { try { Callback.Invoke(); } catch (Exception ex) { Logger?.Error($"Callback error in high-precision timer: {ex}"); } } // === BƯỚC 3: Chờ chính xác đến lần sau === long sleepUntil = NextDueTime; while (!Disposed) { now = Stopwatch.GetTimestamp(); long remaining = sleepUntil - now; if (remaining <= 0) break; // > 1ms → Sleep if (remaining > Stopwatch.Frequency / 1000) { Thread.Sleep(1); } // < 1ms → SpinWait else { Thread.SpinWait((int)(remaining / 10)); } } } } public void Start() { if (!Disposed) { lock (Lock) { if (Interval < 30) HighPrecisionTimerHelper.EnableHighPrecision(); IntervalTicks = (long)(Interval * (Stopwatch.Frequency / 1000.0)); Thread = new Thread(Handler) { IsBackground = true, Priority = ThreadPriority.Highest }; NextDueTime = Stopwatch.GetTimestamp() + IntervalTicks; Thread.Start(); } } else throw new ObjectDisposedException(nameof(HighPrecisionTimer)); } public void Stop() { if (Disposed) return; if (Thread != null) { Disposed = true; lock (Lock) { Thread.Join(100); Thread = null; HighPrecisionTimerHelper.DisableHighPrecision(); } } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (Disposed) return; if (disposing) Stop(); Disposed = true; } ~HighPrecisionTimer() { Dispose(false); } }