Add multi-printer support to label agent
This commit is contained in:
+44
-7
@@ -12,7 +12,7 @@ Alle Endpunkte erfordern einen Bearer Token (JWT oder API-Key):
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
`/jobs/next`, `/jobs/:id/image`, `/jobs/:id/printed` und `/jobs/:id/error` benötigen keine spezifische Permission, nur einen gültigen Token. `POST /jobs` und `POST /preview` erfordern `VIEW_SCANNER`.
|
||||
`/printers/register`, `/jobs/next`, `/jobs/:id/image`, `/jobs/:id/printed` und `/jobs/:id/error` benötigen keine spezifische Permission, nur einen gültigen Token. `POST /jobs` und `POST /preview` erfordern `VIEW_SCANNER`.
|
||||
|
||||
## 1. Job manuell anlegen (Frontend -> Backend)
|
||||
|
||||
@@ -67,7 +67,38 @@ Content-Type: image/png
|
||||
|
||||
Body: binäres PNG-Bild.
|
||||
|
||||
## 3. Nächsten Druckjob abrufen (Agent-Polling)
|
||||
## 3. Drucker registrieren (Agent-Start)
|
||||
|
||||
Der Agent ruft diesen Endpunkt beim Start für jeden konfigurierten lokalen Windows-Drucker auf.
|
||||
|
||||
```http
|
||||
POST /api/label-print-agent/printers/register
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"printerId": "PC-BUERO_ZEBRA_GK420D",
|
||||
"agentId": "PC-BUERO",
|
||||
"name": "Zebra GK420 Büro",
|
||||
"windowsPrinterName": "Zebra GK420d",
|
||||
"dpi": 203,
|
||||
"defaultWidthMm": 101,
|
||||
"defaultHeightMm": 76
|
||||
}
|
||||
```
|
||||
|
||||
### Antwort
|
||||
|
||||
```http
|
||||
200 OK
|
||||
```
|
||||
|
||||
```json
|
||||
{ "ok": true }
|
||||
```
|
||||
|
||||
## 4. Nächsten Druckjob abrufen (Agent-Polling)
|
||||
|
||||
```http
|
||||
GET /api/label-print-agent/jobs/next?agentId={agentId}
|
||||
@@ -92,7 +123,10 @@ Content-Type: application/json
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "42",
|
||||
"jobId": 42,
|
||||
"printerId": "PC-BUERO_ZEBRA_GK420D",
|
||||
"labelType": "artikel",
|
||||
"windowsPrinterName": "Zebra GK420d",
|
||||
"labelImageBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
|
||||
"labelImageContentType": "image/png",
|
||||
"labelWidthMm": 57,
|
||||
@@ -103,13 +137,16 @@ Content-Type: application/json
|
||||
| Feld | Beschreibung |
|
||||
| --- | --- |
|
||||
| `jobId` | Job-ID für Rückmeldungen |
|
||||
| `printerId` | Backend-ID des Zieldruckers |
|
||||
| `labelType` | Etikettart, z. B. `artikel` oder `text` |
|
||||
| `windowsPrinterName` | Exakter lokaler Windows-Druckername, auf den der Agent drucken soll |
|
||||
| `labelImageBase64` | Base64-PNG; `null` wenn Rendering fehlgeschlagen |
|
||||
| `labelImageContentType` | Immer `image/png` |
|
||||
| `labelWidthMm` / `labelHeightMm` | Etikettenmaß in mm |
|
||||
|
||||
Das Backend setzt beim Ausliefern einen Lock (5-Minuten-TTL). Jobs mit abgelaufenem Lock werden erneut angeboten.
|
||||
|
||||
## 4. Etikettbild separat abrufen
|
||||
## 5. Etikettbild separat abrufen
|
||||
|
||||
Alternativ zum Base64-Feld in `jobs/next`.
|
||||
|
||||
@@ -132,7 +169,7 @@ Body: binäres PNG-Bild.
|
||||
|
||||
Job oder Bild nicht vorhanden.
|
||||
|
||||
## 5. Erfolgreichen Druck melden
|
||||
## 6. Erfolgreichen Druck melden
|
||||
|
||||
```http
|
||||
POST /api/label-print-agent/jobs/{jobId}/printed
|
||||
@@ -162,7 +199,7 @@ Alle Felder optional; Fallback jeweils `""` / `unknown`.
|
||||
|
||||
Das Backend setzt den Job auf `printed`, speichert Zeitstempel und ruft die konfigurierte `LabelPrintedUrl` des Templates auf (`POST`).
|
||||
|
||||
## 6. Druckfehler melden
|
||||
## 7. Druckfehler melden
|
||||
|
||||
```http
|
||||
POST /api/label-print-agent/jobs/{jobId}/error
|
||||
@@ -206,7 +243,7 @@ createJob()
|
||||
(PrintedUrl) (ReleaseUrl)
|
||||
```
|
||||
|
||||
## 7. Server-Sent Events – neue Druckaufträge (Push)
|
||||
## 8. Server-Sent Events – neue Druckaufträge (Push)
|
||||
|
||||
```http
|
||||
GET /api/label-print-agent/events
|
||||
|
||||
@@ -38,7 +38,10 @@ Wenn ein Etikett vorhanden ist:
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "12345",
|
||||
"jobId": 12345,
|
||||
"printerId": "PC-BUERO_DYMO_LABELWRITER_450",
|
||||
"labelType": "artikel",
|
||||
"windowsPrinterName": "DYMO LabelWriter 450",
|
||||
"labelImageBase64": "...",
|
||||
"labelImageContentType": "image/png",
|
||||
"labelWidthMm": 57,
|
||||
@@ -50,7 +53,10 @@ Alternativ darf das Backend statt `labelImageBase64` eine URL liefern:
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "12345",
|
||||
"jobId": 12345,
|
||||
"printerId": "PC-BUERO_DYMO_LABELWRITER_450",
|
||||
"labelType": "artikel",
|
||||
"windowsPrinterName": "DYMO LabelWriter 450",
|
||||
"labelImageUrl": "/api/label-print-agent/jobs/12345/image",
|
||||
"labelImageContentType": "image/png",
|
||||
"labelWidthMm": 57,
|
||||
@@ -101,6 +107,7 @@ Beispiel:
|
||||
"baseUrl": "https://paperlessmanager.local",
|
||||
"agentId": "PC-BUERO",
|
||||
"encryptedApiToken": "",
|
||||
"registerPrinterPath": "/api/label-print-agent/printers/register",
|
||||
"nextJobPath": "/api/label-print-agent/jobs/next",
|
||||
"imagePath": "/api/label-print-agent/jobs/{jobId}/image",
|
||||
"reportSuccessPath": "/api/label-print-agent/jobs/{jobId}/printed",
|
||||
@@ -114,6 +121,24 @@ Beispiel:
|
||||
"labelHeightMm": 32,
|
||||
"dpi": 300
|
||||
},
|
||||
"printers": [
|
||||
{
|
||||
"printerId": "PC-BUERO_DYMO_LABELWRITER_450",
|
||||
"name": "DYMO LabelWriter Regal",
|
||||
"windowsPrinterName": "DYMO LabelWriter 450",
|
||||
"dpi": 300,
|
||||
"defaultWidthMm": 57,
|
||||
"defaultHeightMm": 32
|
||||
},
|
||||
{
|
||||
"printerId": "PC-BUERO_ZEBRA_GK420D",
|
||||
"name": "Zebra GK420 Büro",
|
||||
"windowsPrinterName": "Zebra GK420d",
|
||||
"dpi": 203,
|
||||
"defaultWidthMm": 101,
|
||||
"defaultHeightMm": 76
|
||||
}
|
||||
],
|
||||
"worker": {
|
||||
"enabled": true,
|
||||
"pollIntervalSeconds": 30
|
||||
@@ -123,6 +148,8 @@ Beispiel:
|
||||
|
||||
Der API-Token wird lokal mit Windows DPAPI verschlüsselt gespeichert.
|
||||
|
||||
Beim Start registriert der Agent jeden Eintrag aus `printers` beim Backend. Das Feld `printer` bleibt als Kompatibilitätsfeld für ältere Einstellungen erhalten; neue Jobs werden anhand von `windowsPrinterName` aus der Backend-Antwort auf den passenden lokalen Windows-Drucker gedruckt.
|
||||
|
||||
## Dymo-Druck
|
||||
|
||||
Der Dymo LabelWriter muss in Windows als normaler Drucker eingerichtet sein. Das Backend liefert ein fertiges Bild für das Etikett, typischerweise PNG in `57 x 32 mm`.
|
||||
|
||||
@@ -171,13 +171,15 @@ internal sealed class SettingsForm : Form
|
||||
_installUpdateButton.Enabled = false;
|
||||
_lastUpdateCheck = null;
|
||||
|
||||
_labelWidth.Value = settings.Printer.LabelWidthMm;
|
||||
_labelHeight.Value = settings.Printer.LabelHeightMm;
|
||||
var primaryPrinter = GetPrimaryPrinter(settings);
|
||||
_labelWidth.Value = primaryPrinter?.DefaultWidthMm ?? settings.Printer.LabelWidthMm;
|
||||
_labelHeight.Value = primaryPrinter?.DefaultHeightMm ?? settings.Printer.LabelHeightMm;
|
||||
}
|
||||
|
||||
private void LoadPrinters()
|
||||
{
|
||||
var selected = _settingsStore.Load().Printer.PrinterName;
|
||||
var settings = _settingsStore.Load();
|
||||
var selected = GetPrimaryPrinter(settings)?.WindowsPrinterName ?? settings.Printer.PrinterName;
|
||||
_printer.Items.Clear();
|
||||
var printers = _printerService.GetInstalledPrinters();
|
||||
foreach (var printer in printers)
|
||||
@@ -228,9 +230,29 @@ internal sealed class SettingsForm : Form
|
||||
private void SavePrinter(bool showMessage)
|
||||
{
|
||||
var settings = _settingsStore.Load();
|
||||
settings.Printer.PrinterName = GetSelectedPrinterName() ?? string.Empty;
|
||||
var selectedPrinterName = GetSelectedPrinterName() ?? string.Empty;
|
||||
settings.Printer.PrinterName = selectedPrinterName;
|
||||
settings.Printer.LabelWidthMm = _labelWidth.Value;
|
||||
settings.Printer.LabelHeightMm = _labelHeight.Value;
|
||||
if (!string.IsNullOrWhiteSpace(selectedPrinterName))
|
||||
{
|
||||
var existingPrinter = settings.Printers.FirstOrDefault(
|
||||
printer => string.Equals(printer.WindowsPrinterName, selectedPrinterName, StringComparison.OrdinalIgnoreCase));
|
||||
var printerConfig = existingPrinter ?? new PrinterConfig();
|
||||
printerConfig.PrinterId = string.IsNullOrWhiteSpace(printerConfig.PrinterId)
|
||||
? SettingsStore.BuildPrinterId(settings.Backend.AgentId, selectedPrinterName)
|
||||
: printerConfig.PrinterId;
|
||||
printerConfig.Name = string.IsNullOrWhiteSpace(printerConfig.Name) ? selectedPrinterName : printerConfig.Name;
|
||||
printerConfig.WindowsPrinterName = selectedPrinterName;
|
||||
printerConfig.Dpi = settings.Printer.Dpi;
|
||||
printerConfig.DefaultWidthMm = Math.Max(1, (int)Math.Round(_labelWidth.Value));
|
||||
printerConfig.DefaultHeightMm = Math.Max(1, (int)Math.Round(_labelHeight.Value));
|
||||
if (existingPrinter is null)
|
||||
{
|
||||
settings.Printers.Add(printerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
_settingsStore.Save(settings);
|
||||
AppendStatus("Druckereinstellungen gespeichert.");
|
||||
if (showMessage)
|
||||
@@ -357,6 +379,11 @@ internal sealed class SettingsForm : Form
|
||||
};
|
||||
}
|
||||
|
||||
private static PrinterConfig? GetPrimaryPrinter(AppSettings settings)
|
||||
{
|
||||
return settings.Printers.FirstOrDefault(printer => !string.IsNullOrWhiteSpace(printer.WindowsPrinterName));
|
||||
}
|
||||
|
||||
private void AppendStatus(string message)
|
||||
{
|
||||
_status.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}{Environment.NewLine}");
|
||||
|
||||
@@ -112,12 +112,15 @@ internal sealed class TrayApplicationContext : ApplicationContext
|
||||
return (false, "Backend-URL fehlt.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.Printer.PrinterName))
|
||||
var configuredPrinters = settings.Printers
|
||||
.Where(printer => !string.IsNullOrWhiteSpace(printer.WindowsPrinterName))
|
||||
.ToList();
|
||||
if (configuredPrinters.Count == 0)
|
||||
{
|
||||
return (false, "Drucker fehlt.");
|
||||
}
|
||||
|
||||
if (!_printerService.IsPrinterAvailable(settings.Printer.PrinterName))
|
||||
if (!configuredPrinters.Any(printer => _printerService.IsPrinterAvailable(printer.WindowsPrinterName)))
|
||||
{
|
||||
return (false, "Drucker nicht verfügbar.");
|
||||
}
|
||||
|
||||
@@ -35,6 +35,37 @@ public sealed class BackendClient
|
||||
return await response.Content.ReadFromJsonAsync<BackendLabelJob>(cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RegisterPrintersAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var settings = _settingsStore.Load();
|
||||
foreach (var printer in settings.Printers.Where(IsRegisterablePrinter))
|
||||
{
|
||||
var body = new
|
||||
{
|
||||
printerId = printer.PrinterId,
|
||||
agentId = settings.Backend.AgentId,
|
||||
name = printer.Name,
|
||||
windowsPrinterName = printer.WindowsPrinterName,
|
||||
dpi = printer.Dpi,
|
||||
defaultWidthMm = printer.DefaultWidthMm,
|
||||
defaultHeightMm = printer.DefaultHeightMm
|
||||
};
|
||||
|
||||
using var request = CreateRequest(
|
||||
HttpMethod.Post,
|
||||
MakeAbsoluteUrl(settings.Backend.BaseUrl, settings.Backend.RegisterPrinterPath),
|
||||
settings);
|
||||
request.Content = JsonContent.Create(body);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
await EnsureSuccessWithDiagnosticsAsync(response, request, cancellationToken);
|
||||
Log.Information(
|
||||
"Registered printer {PrinterId} ({WindowsPrinterName}) for agent {AgentId}",
|
||||
printer.PrinterId,
|
||||
printer.WindowsPrinterName,
|
||||
settings.Backend.AgentId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Bitmap> GetLabelImageAsync(BackendLabelJob job, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(job.LabelImageBase64))
|
||||
@@ -46,7 +77,7 @@ public sealed class BackendClient
|
||||
if (string.IsNullOrWhiteSpace(job.LabelImageUrl))
|
||||
{
|
||||
var settingsForPath = _settingsStore.Load();
|
||||
job.LabelImageUrl = settingsForPath.Backend.ImagePath.Replace("{jobId}", Uri.EscapeDataString(job.JobId), StringComparison.OrdinalIgnoreCase);
|
||||
job.LabelImageUrl = settingsForPath.Backend.ImagePath.Replace("{jobId}", Uri.EscapeDataString(job.JobId.ToString()), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
var settings = _settingsStore.Load();
|
||||
@@ -57,7 +88,7 @@ public sealed class BackendClient
|
||||
return CreateBitmap(bytesFromUrl);
|
||||
}
|
||||
|
||||
public Task ReportPrintedAsync(string jobId, string printerName, CancellationToken cancellationToken = default)
|
||||
public Task ReportPrintedAsync(int jobId, string printerName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var settings = _settingsStore.Load();
|
||||
var request = new BackendReportRequest
|
||||
@@ -68,7 +99,7 @@ public sealed class BackendClient
|
||||
return PostReportAsync(settings, settings.Backend.ReportSuccessPath, jobId, request, cancellationToken);
|
||||
}
|
||||
|
||||
public Task ReportErrorAsync(string jobId, string printerName, string errorMessage, CancellationToken cancellationToken = default)
|
||||
public Task ReportErrorAsync(int jobId, string printerName, string errorMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var settings = _settingsStore.Load();
|
||||
var request = new BackendReportRequest
|
||||
@@ -80,9 +111,9 @@ public sealed class BackendClient
|
||||
return PostReportAsync(settings, settings.Backend.ReportErrorPath, jobId, request, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task PostReportAsync(AppSettings settings, string pathTemplate, string jobId, BackendReportRequest body, CancellationToken cancellationToken)
|
||||
private async Task PostReportAsync(AppSettings settings, string pathTemplate, int jobId, BackendReportRequest body, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = pathTemplate.Replace("{jobId}", Uri.EscapeDataString(jobId), StringComparison.OrdinalIgnoreCase);
|
||||
var path = pathTemplate.Replace("{jobId}", Uri.EscapeDataString(jobId.ToString()), StringComparison.OrdinalIgnoreCase);
|
||||
using var request = CreateRequest(HttpMethod.Post, MakeAbsoluteUrl(settings.Backend.BaseUrl, path), settings);
|
||||
request.Content = JsonContent.Create(body);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
@@ -217,6 +248,12 @@ public sealed class BackendClient
|
||||
return new Bitmap(stream);
|
||||
}
|
||||
|
||||
private static bool IsRegisterablePrinter(PrinterConfig printer)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(printer.PrinterId)
|
||||
&& !string.IsNullOrWhiteSpace(printer.WindowsPrinterName);
|
||||
}
|
||||
|
||||
private static bool IsLabelJobAvailableEvent(string data)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(data))
|
||||
|
||||
@@ -2,7 +2,10 @@ namespace LabelPrintAgent.Backend;
|
||||
|
||||
public sealed class BackendLabelJob
|
||||
{
|
||||
public string JobId { get; set; } = string.Empty;
|
||||
public int JobId { get; set; }
|
||||
public string PrinterId { get; set; } = string.Empty;
|
||||
public string LabelType { get; set; } = string.Empty;
|
||||
public string WindowsPrinterName { get; set; } = string.Empty;
|
||||
public string? LabelImageBase64 { get; set; }
|
||||
public string? LabelImageUrl { get; set; }
|
||||
public string LabelImageContentType { get; set; } = "image/png";
|
||||
|
||||
@@ -54,11 +54,26 @@ public sealed class BackendPollingWorker : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var printerName = settings.Printer.PrinterName;
|
||||
if (string.IsNullOrWhiteSpace(printerName) || !_printerService.IsPrinterAvailable(printerName))
|
||||
var configuredPrinters = settings.Printers
|
||||
.Where(printer => !string.IsNullOrWhiteSpace(printer.WindowsPrinterName))
|
||||
.ToList();
|
||||
if (configuredPrinters.Count == 0)
|
||||
{
|
||||
Log.Warning("No available printer configured for backend polling");
|
||||
SetStatus(false, "Kein verfügbarer Drucker konfiguriert.");
|
||||
SetStatus(false, "Kein Drucker konfiguriert.");
|
||||
return;
|
||||
}
|
||||
|
||||
await _backendClient.RegisterPrintersAsync(cancellationToken);
|
||||
|
||||
var unavailablePrinters = configuredPrinters
|
||||
.Where(printer => !_printerService.IsPrinterAvailable(printer.WindowsPrinterName))
|
||||
.Select(printer => printer.WindowsPrinterName)
|
||||
.ToList();
|
||||
if (unavailablePrinters.Count == configuredPrinters.Count)
|
||||
{
|
||||
Log.Warning("No configured printer is available for backend polling: {Printers}", string.Join(", ", unavailablePrinters));
|
||||
SetStatus(false, "Kein konfigurierter Drucker verfügbar.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +91,7 @@ public sealed class BackendPollingWorker : IDisposable
|
||||
}
|
||||
|
||||
processedJobs++;
|
||||
await ProcessJobAsync(job, printerName, cancellationToken);
|
||||
await ProcessJobAsync(job, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
@@ -90,10 +105,21 @@ public sealed class BackendPollingWorker : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessJobAsync(BackendLabelJob job, string printerName, CancellationToken cancellationToken)
|
||||
private async Task ProcessJobAsync(BackendLabelJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
var printerName = job.WindowsPrinterName;
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(printerName))
|
||||
{
|
||||
throw new InvalidOperationException("Der Druckjob enthält keinen windowsPrinterName.");
|
||||
}
|
||||
|
||||
if (!_printerService.IsPrinterAvailable(printerName))
|
||||
{
|
||||
throw new InvalidOperationException($"Drucker '{printerName}' ist nicht verfügbar.");
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -110,7 +136,7 @@ public sealed class BackendPollingWorker : IDisposable
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Could not process backend label job {JobId}", job.JobId);
|
||||
await _backendClient.ReportErrorAsync(job.JobId, printerName, ex.Message, cancellationToken);
|
||||
await _backendClient.ReportErrorAsync(job.JobId, printerName ?? string.Empty, ex.Message, cancellationToken);
|
||||
SetStatus(false, $"Job {job.JobId} fehlgeschlagen: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ public sealed class AppSettings
|
||||
public BackendSettings Backend { get; set; } = new();
|
||||
public UpdateSettings Updates { get; set; } = new();
|
||||
public PrinterSettings Printer { get; set; } = new();
|
||||
public List<PrinterConfig> Printers { get; set; } = [];
|
||||
public WorkerSettings Worker { get; set; } = new();
|
||||
public PathSettings Paths { get; set; } = PathSettings.CreateDefault();
|
||||
|
||||
@@ -20,6 +21,7 @@ public sealed class BackendSettings
|
||||
public string BaseUrl { get; set; } = "https://paperlessmanager.local";
|
||||
public string AgentId { get; set; } = Environment.MachineName;
|
||||
public string EncryptedApiToken { get; set; } = string.Empty;
|
||||
public string RegisterPrinterPath { get; set; } = "/api/label-print-agent/printers/register";
|
||||
public string NextJobPath { get; set; } = "/api/label-print-agent/jobs/next";
|
||||
public string ImagePath { get; set; } = "/api/label-print-agent/jobs/{jobId}/image";
|
||||
public string ReportSuccessPath { get; set; } = "/api/label-print-agent/jobs/{jobId}/printed";
|
||||
@@ -43,6 +45,16 @@ public sealed class PrinterSettings
|
||||
public int Dpi { get; set; } = 300;
|
||||
}
|
||||
|
||||
public sealed class PrinterConfig
|
||||
{
|
||||
public string PrinterId { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string WindowsPrinterName { get; set; } = string.Empty;
|
||||
public int Dpi { get; set; } = 203;
|
||||
public int DefaultWidthMm { get; set; } = 57;
|
||||
public int DefaultHeightMm { get; set; } = 32;
|
||||
}
|
||||
|
||||
public sealed class WorkerSettings
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
@@ -41,7 +41,9 @@ public sealed class SettingsStore
|
||||
settings.Backend ??= new BackendSettings();
|
||||
settings.Updates ??= new UpdateSettings();
|
||||
settings.Printer ??= new PrinterSettings();
|
||||
settings.Printers ??= [];
|
||||
settings.Worker ??= new WorkerSettings();
|
||||
MigrateLegacyPrinter(settings);
|
||||
return settings;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -84,4 +86,33 @@ public sealed class SettingsStore
|
||||
|
||||
return normalized.Trim().Trim('"', '\'').Trim();
|
||||
}
|
||||
|
||||
private static void MigrateLegacyPrinter(AppSettings settings)
|
||||
{
|
||||
if (settings.Printers.Count > 0 || string.IsNullOrWhiteSpace(settings.Printer.PrinterName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
settings.Printers.Add(new PrinterConfig
|
||||
{
|
||||
PrinterId = BuildPrinterId(settings.Backend.AgentId, settings.Printer.PrinterName),
|
||||
Name = settings.Printer.PrinterName,
|
||||
WindowsPrinterName = settings.Printer.PrinterName,
|
||||
Dpi = settings.Printer.Dpi,
|
||||
DefaultWidthMm = Math.Max(1, (int)Math.Round(settings.Printer.LabelWidthMm)),
|
||||
DefaultHeightMm = Math.Max(1, (int)Math.Round(settings.Printer.LabelHeightMm))
|
||||
});
|
||||
}
|
||||
|
||||
public static string BuildPrinterId(string agentId, string windowsPrinterName)
|
||||
{
|
||||
var effectiveAgentId = string.IsNullOrWhiteSpace(agentId) ? Environment.MachineName : agentId.Trim();
|
||||
var printerPart = new string((windowsPrinterName ?? string.Empty)
|
||||
.Trim()
|
||||
.Select(character => char.IsLetterOrDigit(character) ? char.ToUpperInvariant(character) : '_')
|
||||
.ToArray());
|
||||
printerPart = string.Join("_", printerPart.Split('_', StringSplitOptions.RemoveEmptyEntries));
|
||||
return $"{effectiveAgentId}_{(string.IsNullOrWhiteSpace(printerPart) ? "PRINTER" : printerPart)}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user