268 lines
8.8 KiB
TypeScript
268 lines
8.8 KiB
TypeScript
import { Printer as EscposPrinter } from '@node-escpos/core';
|
|
import { formatUtils } from './format-utils';
|
|
import { Command, CommandTuple, CommandArray } from './printer-commands';
|
|
import { FONT_SIZES, ALIGNMENT, FONT, STYLE, BARCODE_CONFIG } from './printer-constants';
|
|
import logger from '../logger';
|
|
|
|
export interface CommandExecutor {
|
|
executeCommands(commands: CommandArray): Promise<void>;
|
|
}
|
|
|
|
export class SerialCommandExecutor implements CommandExecutor {
|
|
constructor(private printer: EscposPrinter<[]>) {}
|
|
|
|
async executeCommands(commands: CommandArray): Promise<void> {
|
|
for (const command of commands) {
|
|
await this.executeCommand(command);
|
|
}
|
|
}
|
|
|
|
private async executeCommand(commandTuple: CommandTuple): Promise<void> {
|
|
const [command, ...params] = commandTuple;
|
|
|
|
switch (command) {
|
|
case Command.TEXT:
|
|
await this.printer.text(params[0] as string);
|
|
break;
|
|
|
|
case Command.HEADER:
|
|
await this.printer
|
|
.font(FONT.DEFAULT)
|
|
.align(ALIGNMENT.CENTER)
|
|
.style(STYLE.BOLD)
|
|
.size(FONT_SIZES.LARGE.width, FONT_SIZES.LARGE.height)
|
|
.text(formatUtils.getCheckboxText(formatUtils.checkbox(params[0] as string)));
|
|
break;
|
|
|
|
case Command.BANNER:
|
|
const char = params[0] as string;
|
|
const length = params[1] as number;
|
|
await this.printer.text(formatUtils.bannerString(char, length));
|
|
break;
|
|
|
|
case Command.NEWLINE:
|
|
await this.printer.text('');
|
|
break;
|
|
|
|
case Command.BARCODE:
|
|
const barcodeData = params[0] as string;
|
|
logger.info(`Printing barcode: ${barcodeData} with type: ${BARCODE_CONFIG.TYPE}`);
|
|
|
|
// Try multiple barcode formats until one works
|
|
const barcodeTypes = [BARCODE_CONFIG.TYPE, ...BARCODE_CONFIG.ALTERNATIVE_TYPES];
|
|
let barcodePrinted = false;
|
|
|
|
for (const barcodeType of barcodeTypes) {
|
|
try {
|
|
logger.info(`Trying barcode type: ${barcodeType}`);
|
|
await this.printer.barcode(
|
|
barcodeData,
|
|
barcodeType,
|
|
BARCODE_CONFIG.DIMENSIONS
|
|
);
|
|
logger.info(`Successfully printed barcode with type: ${barcodeType}`);
|
|
barcodePrinted = true;
|
|
break;
|
|
} catch (error) {
|
|
logger.warn(`Barcode type ${barcodeType} failed: ${error}`);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!barcodePrinted) {
|
|
logger.error(`All barcode types failed for data: ${barcodeData}`);
|
|
// As a last resort, just print the data as text
|
|
await this.printer.text(`[BARCODE: ${barcodeData}]`);
|
|
}
|
|
break;
|
|
|
|
case Command.FONT_SIZE:
|
|
const size = params[0] as typeof FONT_SIZES[keyof typeof FONT_SIZES];
|
|
await this.printer.size(size.width, size.height);
|
|
break;
|
|
|
|
case Command.ALIGN:
|
|
await this.printer.align(params[0] as any);
|
|
break;
|
|
|
|
case Command.FONT_FAMILY:
|
|
await this.printer.font(params[0] as any);
|
|
break;
|
|
|
|
case Command.STYLE:
|
|
await this.printer.style(params[0] as any);
|
|
break;
|
|
|
|
case Command.CUT:
|
|
const partial = params[0] as boolean;
|
|
const lines = params[1] as number;
|
|
await this.printer.cut(partial, lines);
|
|
break;
|
|
|
|
case Command.SECTION:
|
|
const header = params[0] as string;
|
|
const content = params[1] as string;
|
|
const bannerChar = params[2] as string;
|
|
const trailingNewline = params[3] as boolean;
|
|
const section = formatUtils.section(header, content, bannerChar, trailingNewline);
|
|
for (const cmd of section) {
|
|
if (cmd[0] === Command.CHECKBOX || cmd[0] === Command.TEXT) {
|
|
await this.printer.text(cmd[1] as string);
|
|
} else if (cmd[0] === Command.BANNER) {
|
|
await this.printer.text(formatUtils.bannerString(cmd[1] as string, cmd[2] as number));
|
|
} else if (cmd[0] === Command.NEWLINE) {
|
|
await this.printer.text('');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Command.STEP_HEADER:
|
|
const stepName = params[0] as string;
|
|
const stepNumber = params[1] as number;
|
|
const taskName = params[2] as string;
|
|
const isTaskView = params[3] as boolean;
|
|
const stepHeader = formatUtils.stepHeader(stepName, stepNumber, taskName, isTaskView);
|
|
await this.printer.text(formatUtils.getCheckboxText(stepHeader));
|
|
break;
|
|
|
|
case Command.CHECKBOX:
|
|
const text = params[0] as string;
|
|
const checkbox = `[ ] ${text}`;
|
|
await this.printer.text(checkbox);
|
|
break;
|
|
|
|
case Command.LIST:
|
|
const items = params[0] as string[];
|
|
const startIndex = params[1] as number;
|
|
const prefix = params[2] as string;
|
|
const listCmds = formatUtils.list(items, startIndex, prefix);
|
|
for (const cmd of listCmds) {
|
|
if (cmd[0] === Command.LIST) {
|
|
// Render as text for test printer, or as needed for real printer
|
|
for (let i = 0; i < items.length; i++) {
|
|
const num = startIndex > 0 ? `${i + startIndex}: ` : '';
|
|
const body = formatUtils.getCheckboxText(formatUtils.checkbox(`${num}${items[i]}`));
|
|
await this.printer.text(`${prefix}${body}`);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Error(`Unknown command: ${command}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
export class TestCommandExecutor implements CommandExecutor {
|
|
private output: string[] = [];
|
|
|
|
async executeCommands(commands: CommandArray): Promise<void> {
|
|
this.output = [];
|
|
|
|
for (const command of commands) {
|
|
await this.executeCommand(command);
|
|
}
|
|
}
|
|
|
|
private async executeCommand(commandTuple: CommandTuple): Promise<void> {
|
|
const [command, ...params] = commandTuple;
|
|
|
|
switch (command) {
|
|
case Command.TEXT:
|
|
this.output.push(params[0] as string);
|
|
break;
|
|
|
|
case Command.HEADER:
|
|
this.output.push(formatUtils.getCheckboxText(formatUtils.checkbox(params[0] as string)));
|
|
break;
|
|
|
|
case Command.BANNER:
|
|
const char = params[0] as string;
|
|
const length = params[1] as number;
|
|
this.output.push(formatUtils.banner(char, length)[1] as string);
|
|
break;
|
|
|
|
case Command.NEWLINE:
|
|
this.output.push('');
|
|
break;
|
|
|
|
case Command.BARCODE:
|
|
this.output.push(`[BARCODE: ${params[0] as string}]`);
|
|
break;
|
|
|
|
case Command.FONT_SIZE:
|
|
// Test printer ignores font size changes
|
|
break;
|
|
|
|
case Command.ALIGN:
|
|
// Test printer ignores alignment changes
|
|
break;
|
|
|
|
case Command.FONT_FAMILY:
|
|
// Test printer ignores font changes
|
|
break;
|
|
|
|
case Command.STYLE:
|
|
// Test printer ignores style changes
|
|
break;
|
|
|
|
case Command.CUT:
|
|
this.output.push('--- CUT ---');
|
|
break;
|
|
|
|
case Command.SECTION:
|
|
const header = params[0] as string;
|
|
const content = params[1] as string;
|
|
const bannerChar = params[2] as string;
|
|
const trailingNewline = params[3] as boolean;
|
|
const section = formatUtils.section(header, content, bannerChar, trailingNewline);
|
|
for (const cmd of section) {
|
|
if (cmd[0] === Command.CHECKBOX || cmd[0] === Command.TEXT) {
|
|
this.output.push(cmd[1] as string);
|
|
} else if (cmd[0] === Command.BANNER) {
|
|
this.output.push(formatUtils.bannerString(cmd[1] as string, cmd[2] as number));
|
|
} else if (cmd[0] === Command.NEWLINE) {
|
|
this.output.push('');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Command.STEP_HEADER:
|
|
const stepName = params[0] as string;
|
|
const stepNumber = params[1] as number;
|
|
const taskName = params[2] as string;
|
|
const isTaskView = params[3] as boolean;
|
|
const stepHeader = formatUtils.stepHeader(stepName, stepNumber, taskName, isTaskView);
|
|
this.output.push(formatUtils.getCheckboxText(stepHeader));
|
|
break;
|
|
|
|
case Command.CHECKBOX:
|
|
this.output.push(formatUtils.getCheckboxText(formatUtils.checkbox(params[0] as string)));
|
|
break;
|
|
|
|
case Command.LIST:
|
|
const items = params[0] as string[];
|
|
const startIndex = params[1] as number;
|
|
const prefix = params[2] as string;
|
|
const listCmds = formatUtils.list(items, startIndex, prefix);
|
|
for (const cmd of listCmds) {
|
|
if (cmd[0] === Command.LIST) {
|
|
for (let i = 0; i < items.length; i++) {
|
|
const num = startIndex > 0 ? `${i + startIndex}: ` : '';
|
|
const body = formatUtils.getCheckboxText(formatUtils.checkbox(`${num}${items[i]}`));
|
|
this.output.push(`${prefix}${body}`);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Error(`Unknown command: ${command}`);
|
|
}
|
|
}
|
|
|
|
getOutput(): string {
|
|
return this.output.join('\n') + '\n';
|
|
}
|
|
} |