using PrintService.Models;
using PrintService.Services;
namespace PrintService;
///
/// Main worker service that coordinates all components
///
public class Worker : BackgroundService
{
private readonly ILogger _logger;
private readonly PrintQueueService _queueService;
private readonly FileMonitorService _fileMonitorService;
private readonly PrinterService _printerService;
private readonly DocumentProcessor _documentProcessor;
private readonly IpcService _ipcService;
public Worker(
ILogger logger,
PrintQueueService queueService,
FileMonitorService fileMonitorService,
PrinterService printerService,
DocumentProcessor documentProcessor,
IpcService ipcService)
{
_logger = logger;
_queueService = queueService;
_fileMonitorService = fileMonitorService;
_printerService = printerService;
_documentProcessor = documentProcessor;
_ipcService = ipcService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("LAAPC Print Service starting at: {time}", DateTimeOffset.Now);
try
{
// Start IPC server for CLI communication
_ipcService.Start();
// Start file monitoring (optional - mainly using IPC for job creation)
// _fileMonitorService.Start();
_logger.LogInformation("Print service started successfully");
_logger.LogInformation("Queue has {Count} pending jobs", _queueService.Count);
// Main processing loop
while (!stoppingToken.IsCancellationRequested)
{
try
{
if (_queueService.TryDequeue(out var job) && job != null)
{
await ProcessJob(job, stoppingToken);
}
else
{
// No jobs in queue, wait a bit
await Task.Delay(500, stoppingToken);
}
}
catch (OperationCanceledException)
{
// Expected during shutdown, exit gracefully
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in processing loop");
try
{
await Task.Delay(1000, stoppingToken);
}
catch (OperationCanceledException)
{
// Shutdown requested during error delay
break;
}
}
}
}
catch (OperationCanceledException)
{
// Expected during shutdown
_logger.LogInformation("Service shutdown requested");
}
catch (Exception ex)
{
_logger.LogCritical(ex, "Fatal error in worker service");
throw;
}
finally
{
_logger.LogInformation("Print service stopping");
_ipcService.Stop();
_fileMonitorService.Stop();
}
}
///
/// Process a single print job
///
private async Task ProcessJob(PrintJob job, CancellationToken cancellationToken)
{
_logger.LogInformation("Processing job {JobId} (Type: {DocType}, Order: {OrderNum})",
job.Id, job.DocumentType, job.OrderNumber ?? "N/A");
job.Status = PrintJobStatus.Processing;
job.UpdatedAt = DateTime.Now;
try
{
// Get document configuration from printer-config.json
var config = _documentProcessor.GetDocumentConfig(job.DocumentType);
if (config == null)
{
throw new InvalidOperationException($"No configuration found for document type '{job.DocumentType}' in printer-config.json");
}
// Read file content
if (!File.Exists(job.QueuedFilePath))
{
throw new FileNotFoundException($"Queued file not found: {job.QueuedFilePath}");
}
var content = _documentProcessor.ReadFile(job.QueuedFilePath);
// Process/transform content
var processedContent = _documentProcessor.ProcessDocument(content, config);
// Print to Windows queue (pass order number for transformations)
await Task.Run(() => _printerService.Print(processedContent, config, job.OriginalFilename, job.OrderNumber), cancellationToken);
// Post-process (archive or delete)
_documentProcessor.PostProcess(job, config);
// Mark as completed
job.Status = PrintJobStatus.Completed;
job.UpdatedAt = DateTime.Now;
_logger.LogInformation("Job {JobId} completed successfully", job.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process job {JobId}", job.Id);
job.LastError = ex.Message;
job.UpdatedAt = DateTime.Now;
// Requeue for retry
_queueService.Requeue(job);
}
}
}