130 lines
4.5 KiB
C#
130 lines
4.5 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading.Channels;
|
|
using Inspectron.Epson.PrintServer.ConfigurationSources;
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Inspectron.Epson.PrintServer.JobSources;
|
|
|
|
public class SignalRPrintJobSource: IPrintJobSource
|
|
{
|
|
private readonly EpsonPrintServiceConfiguration _groupConfiguration;
|
|
private readonly ILogger _logger;
|
|
private readonly HubConnection _connection;
|
|
private readonly Channel<PrintJob> _printJobChannel = Channel.CreateUnbounded<PrintJob>();
|
|
|
|
public SignalRPrintJobSource(EpsonPrintServiceConfiguration groupConfiguration, ILogger logger)
|
|
{
|
|
_groupConfiguration = groupConfiguration;
|
|
_logger = logger;
|
|
|
|
var hubUrl = $"{groupConfiguration.ApiUrl?.TrimEnd('/')}/hubs/printer-servers";
|
|
_connection = new HubConnectionBuilder()
|
|
.WithUrl(hubUrl)
|
|
.WithAutomaticReconnect(new InfiniteRetryPolicy())
|
|
.Build();
|
|
|
|
// Register handlers BEFORE starting connection
|
|
_connection.On<PrintJobFromSignalR>("PrintJob", OnPrintJobReceived);
|
|
|
|
_connection.Reconnecting += OnReconnecting;
|
|
_connection.Reconnected += OnReconnected;
|
|
_connection.Closed += OnClosed;
|
|
|
|
_ = InitializeConnectionAsync();
|
|
}
|
|
|
|
private async Task InitializeConnectionAsync()
|
|
{
|
|
try
|
|
{
|
|
await _connection.StartAsync();
|
|
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
|
_logger.LogInformation("SignalR connection started and joined group {GroupId}.", _groupConfiguration.GroupId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to initialize SignalR connection.");
|
|
}
|
|
}
|
|
|
|
private Task OnReconnecting(Exception? exception)
|
|
{
|
|
_logger.LogWarning(exception, "SignalR connection lost. Reconnecting...");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task OnReconnected(string? connectionId)
|
|
{
|
|
_logger.LogInformation("SignalR reconnected with connection ID: {ConnectionId}. Rejoining group...", connectionId);
|
|
try
|
|
{
|
|
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
|
_logger.LogInformation("Rejoined SignalR group {GroupId} after reconnection.", _groupConfiguration.GroupId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to rejoin group after reconnection.");
|
|
}
|
|
}
|
|
|
|
private async Task OnClosed(Exception? exception)
|
|
{
|
|
_logger.LogError(exception, "SignalR connection closed. Attempting manual restart...");
|
|
|
|
// Manual reconnection loop if automatic reconnection exhausted (shouldn't happen with infinite retry)
|
|
await Task.Delay(5000); // Wait before retry
|
|
try
|
|
{
|
|
await _connection.StartAsync();
|
|
await _connection.InvokeAsync("JoinGroup", _groupConfiguration.GroupId);
|
|
_logger.LogInformation("Manually reconnected to SignalR.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Manual reconnection failed. Will retry on next connection closed event.");
|
|
}
|
|
}
|
|
|
|
private void OnPrintJobReceived(PrintJobFromSignalR args)
|
|
{
|
|
|
|
_logger.LogInformation("Received print job for working area: {PrintJob}", JsonSerializer.Serialize(args));
|
|
_printJobChannel.Writer.TryWrite(new PrintJob
|
|
{
|
|
IP = args.PrinterIp,
|
|
Document = args,
|
|
});
|
|
}
|
|
|
|
public async Task<PrintJob> GetNextJobAsync(CancellationToken cancellationToken)
|
|
{
|
|
return await _printJobChannel.Reader.ReadAsync(cancellationToken);
|
|
}
|
|
|
|
public class PrintJobFromSignalR
|
|
{
|
|
[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; }
|
|
}
|
|
|
|
private class InfiniteRetryPolicy : IRetryPolicy
|
|
{
|
|
public TimeSpan? NextRetryDelay(RetryContext retryContext)
|
|
{
|
|
// Exponential backoff with a cap at 30 seconds
|
|
var delay = Math.Min(Math.Pow(2, retryContext.PreviousRetryCount), 30);
|
|
return TimeSpan.FromSeconds(delay);
|
|
}
|
|
}
|
|
} |