import { Printer } from '@node-escpos/core'; import USB from '@node-escpos/usb-adapter'; import { Task, Step, Printer as PrinterInterface } from '@shared/index'; import { StepRepository, PrintHistoryRepository } from '../db/repositories'; import { Knex } from 'knex'; import logger from '../logger'; export class SerialPrinter implements PrinterInterface { private device: USB | null = null; private printer: Printer<[]> | null = null; private printHistoryRepo: PrintHistoryRepository; private stepRepository: StepRepository; constructor(printHistoryRepo: PrintHistoryRepository, stepRepo: StepRepository) { this.printHistoryRepo = printHistoryRepo; this.stepRepository = stepRepo; this.initializePrinter(); } private async initializePrinter() { try { this.device = new USB(); await new Promise((resolve, reject) => { this.device?.open((err) => { if (err) { logger.error('Failed to open printer:', err); reject(err); return; } resolve(); }); }); const options = { encoding: 'CP437' }; this.printer = new Printer(this.device, options); logger.info('Printer initialized successfully'); } catch (error) { logger.error('Failed to initialize printer:', error); } } async getTaskSteps(_db: Knex, task: Task): Promise { return await this.stepRepository.findByTaskId(task.id); } async printTask(task: Task, db: Knex): Promise { if (!this.printer || !this.device) { throw new Error('Printer not initialized'); } const taskSteps = await this.getTaskSteps(db, task); try { // Print header with task ID as barcode await this.printer .font('a') .align('ct') .style('b') .size(1, 1) // Normal size (0.08 x 2.13 mm) .text(`[ ] Task: ${task.name}`) .text('='.repeat(32)) .text('') .align('lt'); // Print task ID as barcode await this.printer .barcode(task.id.toString(), 'CODE128', { width: 2, height: 50 }) .text('') .text(''); // Print steps for (let i = 0; i < taskSteps.length; i++) { const step = taskSteps[i]; await this.printer .size(1, 1) // Normal size for step header .text(`[ ] Step ${i + 1}: ${step.name}`) .text('-'.repeat(32)) .size(0, 0) // Smaller size for instructions (0.08 x 2.13 mm) .text(step.instructions) .text(''); // Print step ID as barcode await this.printer .barcode(step.id.toString(), 'CODE128', { width: 2, height: 50 }) .text(''); } await this.printer .text('') .text('') .cut() .close(); logger.info(`Printed task ${task.id}`); await this.printHistoryRepo.create({ user_id: 1, // Replace with actual user ID if available task_id: task.id, printed_at: new Date(), }); } catch (error) { logger.error('Failed to print task:', error); throw error; } } async printStep(step: Step, _db: Knex): Promise { if (!this.printer || !this.device) { throw new Error('Printer not initialized'); } try { await this.printer .font('a') .align('ct') .style('b') .size(1, 1) // Normal size (0.08 x 2.13 mm) .text(`[ ] Step: ${step.name}`) .text('='.repeat(32)) .text('') .align('lt') .size(0, 0) // Smaller size for instructions .text(step.instructions) .text('') .text(''); // Print step ID as barcode await this.printer .barcode(step.id.toString(), 'CODE128', { width: 2, height: 50 }) .text('') .text('') .cut() .close(); logger.info(`Printed step ${step.id}`); await this.printHistoryRepo.create({ user_id: 1, // Replace with actual user ID if available step_id: step.id, printed_at: new Date(), }); } catch (error) { logger.error('Failed to print step:', error); throw error; } } }