feat: add printer connector with job queue
This commit is contained in:
parent
400b716623
commit
002f6dc8f3
134
src/printer/connector.ts
Normal file
134
src/printer/connector.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
interface PrinterConfig {
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrintJob {
|
||||||
|
id: string;
|
||||||
|
data: Uint8Array;
|
||||||
|
status: 'pending' | 'printing' | 'completed' | 'failed';
|
||||||
|
createdAt: Date;
|
||||||
|
startedAt?: Date;
|
||||||
|
completedAt?: Date;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PrinterConnector {
|
||||||
|
private config: PrinterConfig;
|
||||||
|
private queueItems: PrintJob[] = [];
|
||||||
|
private currentJob: PrintJob | null = null;
|
||||||
|
private connected = false;
|
||||||
|
private processing = false;
|
||||||
|
|
||||||
|
constructor(config: PrinterConfig) {
|
||||||
|
this.config = {
|
||||||
|
timeout: 5000,
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnected(): boolean {
|
||||||
|
return this.connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueueLength(): number {
|
||||||
|
return this.queueItems.length + (this.currentJob ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue(data: Uint8Array): string {
|
||||||
|
const job: PrintJob = {
|
||||||
|
id: `job_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
|
data,
|
||||||
|
status: 'pending',
|
||||||
|
createdAt: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
this.queueItems.push(job);
|
||||||
|
this.processQueue();
|
||||||
|
|
||||||
|
return job.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
getJob(jobId: string): PrintJob | undefined {
|
||||||
|
if (this.currentJob?.id === jobId) return this.currentJob;
|
||||||
|
return this.queueItems.find(j => j.id === jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllJobs(): PrintJob[] {
|
||||||
|
return this.currentJob
|
||||||
|
? [this.currentJob, ...this.queueItems]
|
||||||
|
: [...this.queueItems];
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelJob(jobId: string): boolean {
|
||||||
|
const index = this.queueItems.findIndex(j => j.id === jobId);
|
||||||
|
if (index >= 0 && this.queueItems[index].status === 'pending') {
|
||||||
|
this.queueItems[index].status = 'cancelled' as any;
|
||||||
|
this.queueItems.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async processQueue(): Promise<void> {
|
||||||
|
if (this.processing || this.queueItems.length === 0) return;
|
||||||
|
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
while (this.queueItems.length > 0) {
|
||||||
|
const job = this.queueItems.shift()!;
|
||||||
|
this.currentJob = job;
|
||||||
|
job.status = 'printing';
|
||||||
|
job.startedAt = new Date();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.sendToPrinter(job.data);
|
||||||
|
job.status = 'completed';
|
||||||
|
job.completedAt = new Date();
|
||||||
|
} catch (error) {
|
||||||
|
job.status = 'failed';
|
||||||
|
job.error = String(error);
|
||||||
|
console.error(`Print job ${job.id} failed:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentJob = null;
|
||||||
|
this.processing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendToPrinter(data: Uint8Array): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const socket = new (Bun as any).connect({
|
||||||
|
hostname: this.config.ip,
|
||||||
|
port: this.config.port,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.then((conn: any) => {
|
||||||
|
this.connected = true;
|
||||||
|
conn.write(data);
|
||||||
|
conn.end();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.connected = false;
|
||||||
|
resolve();
|
||||||
|
}, 1000);
|
||||||
|
}).catch((err: any) => {
|
||||||
|
this.connected = false;
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(): Promise<{ online: boolean; paperStatus: string }> {
|
||||||
|
try {
|
||||||
|
const socket = await (Bun as any).connect({
|
||||||
|
hostname: this.config.ip,
|
||||||
|
port: this.config.port,
|
||||||
|
});
|
||||||
|
socket.end();
|
||||||
|
return { online: true, paperStatus: 'ok' };
|
||||||
|
} catch {
|
||||||
|
return { online: false, paperStatus: 'unknown' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
tests/connector.test.ts
Normal file
25
tests/connector.test.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { describe, it, expect } from 'bun:test';
|
||||||
|
import { PrinterConnector } from '../src/printer/connector';
|
||||||
|
|
||||||
|
describe('Printer Connector', () => {
|
||||||
|
it('should create connector with config', () => {
|
||||||
|
const connector = new PrinterConnector({
|
||||||
|
ip: '192.168.1.100',
|
||||||
|
port: 9100
|
||||||
|
});
|
||||||
|
expect(connector).toBeDefined();
|
||||||
|
expect(connector.isConnected()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should queue print jobs', () => {
|
||||||
|
const connector = new PrinterConnector({
|
||||||
|
ip: '192.168.1.100',
|
||||||
|
port: 9100
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = new Uint8Array([0x1B, 0x40]);
|
||||||
|
const jobId = connector.queue(data);
|
||||||
|
expect(jobId).toMatch(/^job_/);
|
||||||
|
expect(connector.getQueueLength()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user