2025-06-14 22:27:33 +00:00
|
|
|
import { Printer } from "@node-escpos/core";
|
|
|
|
import USB from "@node-escpos/usb-adapter";
|
|
|
|
import { Task, Step, Printer as PrinterInterface } from '@shared/index';
|
2025-06-14 23:04:05 +00:00
|
|
|
import { StepRepository, PrintHistoryRepository } from '../db/repositories';
|
2025-06-14 22:27:33 +00:00
|
|
|
import { Knex } from 'knex';
|
|
|
|
import logger from '../logger';
|
|
|
|
|
|
|
|
export class SerialPrinter implements PrinterInterface {
|
|
|
|
private device: USB | null = null;
|
|
|
|
private printer: Printer<[]> | null = null;
|
2025-06-14 23:04:05 +00:00
|
|
|
private printHistoryRepo: PrintHistoryRepository;
|
|
|
|
private stepRepository: StepRepository;
|
2025-06-14 22:27:33 +00:00
|
|
|
|
2025-06-14 23:04:05 +00:00
|
|
|
constructor(printHistoryRepo: PrintHistoryRepository, stepRepo: StepRepository) {
|
|
|
|
this.printHistoryRepo = printHistoryRepo;
|
|
|
|
this.stepRepository = stepRepo;
|
2025-06-14 22:27:33 +00:00
|
|
|
this.initializePrinter();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async initializePrinter() {
|
|
|
|
try {
|
|
|
|
this.device = new USB();
|
|
|
|
await new Promise<void>((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<Step[]> {
|
2025-06-14 23:04:05 +00:00
|
|
|
return await this.stepRepository.findByTaskId(task.id);
|
2025-06-14 22:27:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async printTask(task: Task, db: Knex): Promise<void> {
|
|
|
|
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}`);
|
2025-06-14 23:04:05 +00:00
|
|
|
|
|
|
|
await this.printHistoryRepo.create({
|
|
|
|
user_id: 1, // Replace with actual user ID if available
|
|
|
|
task_id: task.id,
|
|
|
|
printed_at: new Date(),
|
|
|
|
});
|
2025-06-14 22:27:33 +00:00
|
|
|
} catch (error) {
|
|
|
|
logger.error('Failed to print task:', error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async printStep(step: Step, db: Knex): Promise<void> {
|
|
|
|
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}`);
|
2025-06-14 23:04:05 +00:00
|
|
|
|
|
|
|
await this.printHistoryRepo.create({
|
|
|
|
user_id: 1, // Replace with actual user ID if available
|
|
|
|
step_id: step.id,
|
|
|
|
printed_at: new Date(),
|
|
|
|
});
|
2025-06-14 22:27:33 +00:00
|
|
|
} catch (error) {
|
|
|
|
logger.error('Failed to print step:', error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|