import 'dart:async'; import '../print/backend.dart'; import '../print/cups_backend.dart' show noopLog; import '../utils/command_runner.dart'; import 'protocol.dart'; /// 打印队列中的单个任务运行时状态。 class PrintQueueTask { PrintQueueTask({required this.taskId, required this.request}); final String taskId; final PrintRequest request; /// printing / done / error String state = 'printing'; int status = 0; String error = ''; DateTime? finishedAt; } /// 串行打印队列:打印机物理上一次只能打一张卡,任务逐个执行。 /// /// 任务状态存内存字典供前端轮询;完成后保留 30 分钟(对齐 C# 版 PrintQueue)。 class PrintQueue { PrintQueue(this._backend, {void Function(String line)? onLog}) : _log = onLog ?? noopLog { _cleanupTimer = Timer.periodic(const Duration(minutes: 5), (_) => _cleanupOld()); } final PrintBackend _backend; final void Function(String line) _log; final _tasks = {}; Future _tail = Future.value(); Timer? _cleanupTimer; int _seq = 0; /// 提交打印任务,立即返回 taskId。 String submit(PrintRequest request) { if (request.front == null || request.front!.color.isEmpty) { throw ArgumentError('缺少正面卡面图(front.color)'); } final task = PrintQueueTask(taskId: 't${++_seq}', request: request); _tasks[task.taskId] = task; // 串行链:每个任务等前一个完成后执行,异常在 _run 内消化,链条不中断 _tail = _tail.then((_) => _run(task)); return task.taskId; } PrintTaskDto? get(String taskId) { final task = _tasks[taskId]; if (task == null) return null; return PrintTaskDto(taskId: task.taskId, state: task.state, status: task.status, statusText: task.error); } Future _run(PrintQueueTask task) async { _log('[${task.taskId}] 开始打印(份数 ${task.request.copies})'); try { await _backend.printOneCard(task.request); task.state = 'done'; _log('[${task.taskId}] 打印完成'); } catch (e) { task.state = 'error'; task.error = errorMessageOf(e); _log('[${task.taskId}] 打印失败:${task.error}'); } finally { task.finishedAt = DateTime.now(); } } /// 清掉已完成/失败超 30 分钟的任务,避免内存常驻增长。 void _cleanupOld() { final now = DateTime.now(); _tasks.removeWhere((_, task) => task.finishedAt != null && now.difference(task.finishedAt!).inMinutes > 30); } void dispose() { _cleanupTimer?.cancel(); _cleanupTimer = null; } /// 从任意异常里提取可读文案(前端 statusText 直接展示)。 static String errorMessageOf(Object e) { if (e is CommandException) return e.message; try { final dynamic err = e; final msg = err.message; if (msg is String && msg.isNotEmpty) return msg; } catch (_) {} return e.toString(); } }