feat: 基础
This commit is contained in:
193
Worker.cs
193
Worker.cs
@@ -4,29 +4,212 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Cloudprint;
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace cloudPrintWinAgent
|
||||
{
|
||||
public sealed class Worker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<Worker> _logger;
|
||||
|
||||
public Worker(ILogger<Worker> logger)
|
||||
|
||||
private readonly CloudPrintOptions _opt;
|
||||
|
||||
public Worker(ILogger<Worker> logger, IOptions<CloudPrintOptions> cloudPrintOptions)
|
||||
{
|
||||
_logger = logger;
|
||||
_opt = cloudPrintOptions.Value;
|
||||
ValidateOptions();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("WinAgent started at: {Time}", DateTimeOffset.Now);
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
|
||||
// Go 后端是明文 h2c(http://),需要打开这个开关
|
||||
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
|
||||
|
||||
|
||||
using var channel = GrpcChannel.ForAddress(_opt.ServerAddress);
|
||||
var client = new PrintService.PrintServiceClient(channel);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: 后续放心跳、订阅任务、状态回传
|
||||
_logger.LogInformation("WinAgent heartbeat tick at: {Time}", DateTimeOffset.Now);
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
||||
var (printerId, token) = await RegisterPrinterAsync(client, stoppingToken);
|
||||
|
||||
var heartbeatTask = HeartbeatLoopAsync(client, printerId, token, stoppingToken);
|
||||
var subscribeTask = SubscribeJobsLoopAsync(client, printerId, token, stoppingToken);
|
||||
|
||||
await Task.WhenAll(heartbeatTask, subscribeTask);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Worker fatal error");
|
||||
}
|
||||
|
||||
|
||||
_logger.LogInformation("WinAgent stopping at: {Time}", DateTimeOffset.Now);
|
||||
}
|
||||
|
||||
private async Task<(int printerId, string token)> RegisterPrinterAsync(
|
||||
PrintService.PrintServiceClient client,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var resp = await client.RegisterPrinterAsync(new RegisterPrinterRequest
|
||||
{
|
||||
Name = _opt.PrinterName,
|
||||
DisplayName = _opt.PrinterDisplayName,
|
||||
ApiKey = _opt.ApiKey
|
||||
}, cancellationToken: ct);
|
||||
|
||||
_logger.LogInformation("Registered printer. PrinterId={PrinterId}", resp.PrinterId);
|
||||
return (resp.PrinterId, resp.Token);
|
||||
}
|
||||
|
||||
// 0: 空闲 1: 打印中 2: 离线
|
||||
private async Task HeartbeatLoopAsync(
|
||||
PrintService.PrintServiceClient client,
|
||||
int printerId,
|
||||
string token,
|
||||
CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.PrinterHeartbeatAsync(new PrinterHeartbeatRequest
|
||||
{
|
||||
PrinterId = printerId,
|
||||
Status = 0,
|
||||
Token = token
|
||||
}, cancellationToken: ct);
|
||||
|
||||
_logger.LogInformation("Heartbeat ok: {Time}", DateTimeOffset.Now);
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Heartbeat rpc failed");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), ct);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SubscribeJobsLoopAsync(
|
||||
PrintService.PrintServiceClient client,
|
||||
int printerId,
|
||||
string token,
|
||||
CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var call = client.SubscribeJobs(new SubscribeJobsRequest
|
||||
{
|
||||
PrinterId = printerId,
|
||||
Token = token
|
||||
}, cancellationToken: ct);
|
||||
|
||||
await foreach (var job in call.ResponseStream.ReadAllAsync(ct))
|
||||
{
|
||||
await HandleJobAsync(client, job, ct);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Subscribe stream disconnected, retry in 3s");
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Subscribe loop error, retry in 3s");
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleJobAsync(
|
||||
PrintService.PrintServiceClient client,
|
||||
PrintJob job,
|
||||
CancellationToken ct)
|
||||
{
|
||||
_logger.LogInformation("Received job: JobNo={JobNo}, FilePath={FilePath}",
|
||||
job.JobNo, job.OrderItem?.File?.FilePath);
|
||||
|
||||
try
|
||||
{
|
||||
await ReportStatusAsync(client, job.JobNo, "printing", 10, "", ct);
|
||||
|
||||
// 模拟打印,后续替换成真实 WinAPI 打印
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), ct);
|
||||
|
||||
await ReportStatusAsync(client, job.JobNo, "completed", 100, "", ct);
|
||||
_logger.LogInformation("Job completed: {JobNo}", job.JobNo);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 服务停止/任务取消
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Job failed: {JobNo}", job.JobNo);
|
||||
await ReportStatusAsync(client, job.JobNo, "failed", 0, ex.Message, ct);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReportStatusAsync(
|
||||
PrintService.PrintServiceClient client,
|
||||
string jobNo,
|
||||
string status,
|
||||
int progress,
|
||||
string errorMsg,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await client.ReportJobStatusAsync(new ReportJobStatusRequest
|
||||
{
|
||||
JobNo = jobNo,
|
||||
Status = status,
|
||||
Progress = progress,
|
||||
ErrorMsg = errorMsg ?? string.Empty
|
||||
}, cancellationToken: ct);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void ValidateOptions()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_opt.ServerAddress))
|
||||
throw new InvalidOperationException("CloudPrint:ServerAddress 不能为空");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_opt.PrinterName))
|
||||
throw new InvalidOperationException("CloudPrint:PrinterName 不能为空");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_opt.PrinterDisplayName))
|
||||
throw new InvalidOperationException("CloudPrint:PrinterDisplayName 不能为空");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_opt.ApiKey))
|
||||
throw new InvalidOperationException("CloudPrint:ApiKey 不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user