From 91876040f924a90a5de5da72283da312a0717c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20P=C3=B6ttker?= Date: Tue, 26 May 2026 09:04:22 +0200 Subject: [PATCH] Drain backend print queue per poll --- BACKEND_API.md | 2 +- README.md | 6 +- .../Backend/BackendPollingWorker.cs | 56 ++++++++++++------- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/BACKEND_API.md b/BACKEND_API.md index fe7b208..a7edb75 100644 --- a/BACKEND_API.md +++ b/BACKEND_API.md @@ -220,7 +220,7 @@ Der Agent verbindet sich einmalig. Sobald ein neuer Druckauftrag erstellt wird, data: {"type":"label-job-available"} ``` -Der Agent ruft daraufhin sofort `GET /jobs/next` auf. Polling bleibt als Fallback sinnvoll, z. B. alle 30 Sekunden, falls die SSE-Verbindung unterbrochen wurde. +Der Agent ruft daraufhin sofort `GET /jobs/next` auf. Ein Poll-Lauf ruft `/jobs/next` wiederholt auf, bis das Backend `204 No Content` zurückgibt. Polling bleibt als Fallback sinnvoll, z. B. alle 30 Sekunden, falls die SSE-Verbindung unterbrochen wurde. Es werden keine `agentId`-Parameter ausgewertet; alle verbundenen Agents erhalten das Event. diff --git a/README.md b/README.md index 114db64..a4e50cd 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@ Der Agent speichert keine Layouts, keine Nummernserver-Regeln und keine MySQL-Qu ```text LabelPrintAgent -> fragt alle X Sekunden PaperlessManager-Backend --> erhält einen druckfertigen Etikettenjob +-> erhält druckfertige Etikettenjobs, bis kein Job mehr vorhanden ist -> lädt/liest das Etikettbild -> druckt über Windows-Drucker -> meldet Erfolg oder Fehler ans Backend zurück ``` -Der Agent unterstützt Server-Sent Events für Push-Benachrichtigungen und nutzt Polling als Fallback. +Der Agent unterstützt Server-Sent Events für Push-Benachrichtigungen und nutzt Polling als Fallback. Jeder Poll-Lauf ruft so lange `/jobs/next` auf, bis das Backend `204 No Content` zurückgibt. ## Backend-Vertrag @@ -141,7 +141,7 @@ Wichtig: 3. Im Tab `Backend` BaseUrl, AgentId und optional API-Token eintragen. 4. Im Tab `Drucker` den Dymo LabelWriter auswählen. 5. Im Tab `Allgemein` Polling aktivieren und Intervall setzen. -6. Mit `Jetzt prüfen` kann sofort ein einzelner Backend-Poll ausgelöst werden. +6. Mit `Jetzt prüfen` kann sofort ein Poll-Lauf ausgelöst werden; dabei werden alle aktuell verfügbaren Jobs verarbeitet. ## Tray-Status diff --git a/src/LabelPrintAgent/Backend/BackendPollingWorker.cs b/src/LabelPrintAgent/Backend/BackendPollingWorker.cs index 2240547..9bc6673 100644 --- a/src/LabelPrintAgent/Backend/BackendPollingWorker.cs +++ b/src/LabelPrintAgent/Backend/BackendPollingWorker.cs @@ -62,32 +62,21 @@ public sealed class BackendPollingWorker : IDisposable return; } - var job = await _backendClient.GetNextJobAsync(cancellationToken); - if (job is null) + var processedJobs = 0; + while (!cancellationToken.IsCancellationRequested) { - SetStatus(true, "Backend verbunden. Kein Druckjob vorhanden."); - return; - } - - try - { - using var bitmap = await _backendClient.GetLabelImageAsync(job, cancellationToken); - var result = await _printerService.PrintAsync(bitmap, printerName, job.LabelWidthMm, job.LabelHeightMm, cancellationToken); - if (result.Success) + var job = await _backendClient.GetNextJobAsync(cancellationToken); + if (job is null) { - await _backendClient.ReportPrintedAsync(job.JobId, printerName, cancellationToken); - SetStatus(true, $"Job {job.JobId} erfolgreich gedruckt."); + var message = processedJobs == 0 + ? "Backend verbunden. Kein Druckjob vorhanden." + : $"Backend verbunden. {processedJobs} Druckjob(s) verarbeitet. Keine weiteren Druckjobs vorhanden."; + SetStatus(true, message); return; } - await _backendClient.ReportErrorAsync(job.JobId, printerName, result.ErrorMessage ?? "Druck fehlgeschlagen.", cancellationToken); - SetStatus(false, $"Job {job.JobId} konnte nicht gedruckt werden."); - } - catch (Exception ex) - { - Log.Error(ex, "Could not process backend label job {JobId}", job.JobId); - await _backendClient.ReportErrorAsync(job.JobId, printerName, ex.Message, cancellationToken); - SetStatus(false, $"Job {job.JobId} fehlgeschlagen: {ex.Message}"); + processedJobs++; + await ProcessJobAsync(job, printerName, cancellationToken); } } catch (Exception ex) when (ex is not OperationCanceledException) @@ -101,6 +90,31 @@ public sealed class BackendPollingWorker : IDisposable } } + private async Task ProcessJobAsync(BackendLabelJob job, string printerName, CancellationToken cancellationToken) + { + try + { + SetStatus(true, $"Job {job.JobId} wird gedruckt."); + using var bitmap = await _backendClient.GetLabelImageAsync(job, cancellationToken); + var result = await _printerService.PrintAsync(bitmap, printerName, job.LabelWidthMm, job.LabelHeightMm, cancellationToken); + if (result.Success) + { + await _backendClient.ReportPrintedAsync(job.JobId, printerName, cancellationToken); + SetStatus(true, $"Job {job.JobId} erfolgreich gedruckt."); + return; + } + + await _backendClient.ReportErrorAsync(job.JobId, printerName, result.ErrorMessage ?? "Druck fehlgeschlagen.", cancellationToken); + SetStatus(false, $"Job {job.JobId} konnte nicht gedruckt werden."); + } + catch (Exception ex) + { + Log.Error(ex, "Could not process backend label job {JobId}", job.JobId); + await _backendClient.ReportErrorAsync(job.JobId, printerName, ex.Message, cancellationToken); + SetStatus(false, $"Job {job.JobId} fehlgeschlagen: {ex.Message}"); + } + } + private async Task RunAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested)