Compare commits
2 Commits
main
...
eb49cc64a7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb49cc64a7 | ||
|
|
e7a6dc8b33 |
@@ -3,7 +3,6 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
using cloudPrintWinAgent;
|
using cloudPrintWinAgent;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
@@ -15,9 +14,6 @@ builder.Services.AddWindowsService(options =>
|
|||||||
|
|
||||||
builder.Services.AddHostedService<Worker>();
|
builder.Services.AddHostedService<Worker>();
|
||||||
|
|
||||||
builder.Services.Configure<CloudPrintOptions>(
|
|
||||||
builder.Configuration.GetSection("CloudPrint"));
|
|
||||||
|
|
||||||
builder.Logging.ClearProviders();
|
builder.Logging.ClearProviders();
|
||||||
|
|
||||||
if (Environment.UserInteractive)
|
if (Environment.UserInteractive)
|
||||||
|
|||||||
193
Worker.cs
193
Worker.cs
@@ -4,212 +4,29 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Cloudprint;
|
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Net.Client;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace cloudPrintWinAgent
|
namespace cloudPrintWinAgent
|
||||||
{
|
{
|
||||||
public sealed class Worker : BackgroundService
|
public sealed class Worker : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly ILogger<Worker> _logger;
|
private readonly ILogger<Worker> _logger;
|
||||||
|
|
||||||
|
public Worker(ILogger<Worker> logger)
|
||||||
private readonly CloudPrintOptions _opt;
|
|
||||||
|
|
||||||
public Worker(ILogger<Worker> logger, IOptions<CloudPrintOptions> cloudPrintOptions)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_opt = cloudPrintOptions.Value;
|
|
||||||
ValidateOptions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("WinAgent started at: {Time}", DateTimeOffset.Now);
|
_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
|
|
||||||
{
|
{
|
||||||
var (printerId, token) = await RegisterPrinterAsync(client, stoppingToken);
|
// TODO: 后续放心跳、订阅任务、状态回传
|
||||||
|
_logger.LogInformation("WinAgent heartbeat tick at: {Time}", DateTimeOffset.Now);
|
||||||
var heartbeatTask = HeartbeatLoopAsync(client, printerId, token, stoppingToken);
|
await Task.Delay(TimeSpan.FromSeconds(10), 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);
|
_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 不能为空");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"CloudPrint": {
|
|
||||||
"ServerAddress": "http://127.0.0.1:8080",
|
|
||||||
"PrinterName": "win-printer-001",
|
|
||||||
"PrinterDisplayName": "Windows Printer 001",
|
|
||||||
"ApiKey": "replace-with-your-api-key"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,13 +18,4 @@
|
|||||||
<Protobuf Include=".\proto\cloudprint.proto" GrpcServices="Client" />
|
<Protobuf Include=".\proto\cloudprint.proto" GrpcServices="Client" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="appsettings.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace cloudPrintWinAgent
|
|
||||||
{
|
|
||||||
public sealed class CloudPrintOptions
|
|
||||||
{
|
|
||||||
public string ServerAddress { get; set; } = "http://127.0.0.1:8080";
|
|
||||||
public string PrinterName { get; set; } = "win-printer-001";
|
|
||||||
public string PrinterDisplayName { get; set; } = "Windows Printer 001";
|
|
||||||
public string ApiKey { get; set; } = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user