using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Channels; using Inspectron.Epson.PrintServer.ConfigurationSources; using Inspectron.Epson.Queue; using Microsoft.Extensions.Logging; namespace Inspectron.Epson.PrintServer.JobSources; public class HttpPollingPrintJobSource : IPrintJobSource, IDisposable { private readonly EpsonPrintServiceConfiguration _groupConfiguration; private readonly ILogger _logger; private readonly HttpClient _httpClient; private readonly Channel _printJobChannel = Channel.CreateUnbounded(); private readonly CancellationTokenSource _pollingCts = new(); private readonly TimeSpan _pollingInterval; public HttpPollingPrintJobSource( EpsonPrintServiceConfiguration groupConfiguration, ILogger logger ) { _groupConfiguration = groupConfiguration; _logger = logger; _pollingInterval = TimeSpan.FromSeconds(3); _httpClient = new HttpClient { BaseAddress = new Uri(groupConfiguration.ApiUrl?.TrimEnd('/') + "/") }; _httpClient.DefaultRequestHeaders.Add("X-Printer-Server-Key", groupConfiguration.ApiKey); } private async Task StartPollingAsync(CancellationToken cancellationToken) { _logger.LogInformation("Starting HTTP polling for print jobs at {BaseUrl}", _httpClient.BaseAddress); while (!cancellationToken.IsCancellationRequested) { try { await PollForJobsAsync(cancellationToken); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { break; } catch (Exception ex) { _logger.LogError(ex, "Error polling for print jobs. Retrying in {Interval}...", _pollingInterval); } try { await Task.Delay(_pollingInterval, cancellationToken); } catch (OperationCanceledException) { break; } } _logger.LogInformation("HTTP polling stopped."); } private async Task PollForJobsAsync(CancellationToken cancellationToken) { var requestUrl = $"/api/printerServer/jobs"; var response = await _httpClient.GetAsync(requestUrl, cancellationToken); if (!response.IsSuccessStatusCode) { _logger.LogWarning("Failed to fetch jobs. Status: {StatusCode}", response.StatusCode); return; } var jobs = await response.Content.ReadFromJsonAsync>(cancellationToken: cancellationToken); if (jobs == null || jobs.Count == 0) { return; } _logger.LogInformation("Received {Count} print job(s) from HTTP endpoint.", jobs.Count); foreach (var job in jobs) { _logger.LogInformation("Queuing print job: {PrintJob}", JsonSerializer.Serialize(job)); _printJobChannel.Writer.TryWrite(new PrintJob { JobId = job.JobId, IP = job.PrinterIp, Document = new SignalRPrintJobSource.PrintJobFromSignalR { JobId = job.JobId, PrinterIp = job.PrinterIp, LogoUrl = job.LogoUrl, ReceiptType = job.ReceiptType, Content = job.Content } }); } } public Task StartAsync() { return StartPollingAsync(_pollingCts.Token); } public async Task GetNextJobAsync(CancellationToken cancellationToken) { return await _printJobChannel.Reader.ReadAsync(cancellationToken); } public void Dispose() { _pollingCts.Cancel(); _pollingCts.Dispose(); _httpClient.Dispose(); } public class PrintJobDto { [JsonPropertyName("jobId")] public string JobId { get; set; } = ""; [JsonPropertyName("printerIp")] public string PrinterIp { get; set; } = ""; [JsonPropertyName("logoUrl")] public string? LogoUrl { get; set; } [JsonPropertyName("receiptType")] public int ReceiptType { get; set; } [JsonPropertyName("content")] public string Content { get; set; } = ""; } }