using Microsoft.Extensions.Logging; using RobotNet10.CANOpen.Enums; using RobotNet10.CANOpen.Interfaces; namespace RobotNet10.CANOpen.Services; /// /// SYNC Producer - phát tin nhắn SYNC định kỳ cho synchronous PDOs /// public class SyncProducer : IDisposable { private readonly ICanBus _canBus; private readonly uint _cobId; private readonly ILogger? _logger; private Timer? _timer; private byte _counter; private bool _useCounter; private bool _isRunning; public bool IsRunning => _isRunning; public int IntervalMs { get; private set; } public SyncProducer(ICanBus canBus, uint cobId = (uint)CanMessageType.Sync, ILogger? logger = null) { _canBus = canBus ?? throw new ArgumentNullException(nameof(canBus)); _cobId = cobId; _logger = logger; _counter = 0; _useCounter = false; _isRunning = false; } /// /// Start SYNC producer với interval tính bằng milliseconds /// /// SYNC interval in milliseconds (thường 1-100ms) /// Nếu true, SYNC message sẽ có counter byte (1-240) public void Start(int intervalMs, bool useCounter = false) { if (_isRunning) Stop(); IntervalMs = intervalMs; _useCounter = useCounter; _counter = 0; _isRunning = true; _timer = new Timer(SendSyncCallback, null, 0, intervalMs); } /// /// Stop SYNC producer /// public void Stop() { _timer?.Dispose(); _timer = null; _isRunning = false; _counter = 0; } /// /// Gửi một SYNC message thủ công (không dùng timer) /// public async Task SendSyncAsync(CancellationToken ct = default) { byte[] data = Array.Empty(); if (_useCounter) { _counter++; if (_counter > 240) _counter = 1; data = new byte[] { _counter }; } await _canBus.SendFrameAsync(_cobId, data, ct); } private async void SendSyncCallback(object? state) { try { await SendSyncAsync(CancellationToken.None); } catch (Exception ex) { // Log error nhưng không dừng timer _logger?.LogError(ex, "Failed to send SYNC message with COB-ID 0x{CobId:X3}", _cobId); } } public void Dispose() { Stop(); } }