taro-bluetooth-print 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -6
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1678 -643
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/BaseAdapter.d.ts +4 -4
- package/dist/types/core/BluetoothPrinter.d.ts +16 -17
- package/dist/types/core/EventEmitter.d.ts +38 -1
- package/dist/types/services/CommandBuilder.d.ts +73 -0
- package/dist/types/services/ConnectionManager.d.ts +53 -0
- package/dist/types/services/PrintJobManager.d.ts +82 -0
- package/dist/types/services/interfaces/index.d.ts +161 -0
- package/dist/types/utils/encoding.d.ts +25 -1
- package/dist/types/utils/logger.d.ts +67 -10
- package/dist/types/utils/platform.d.ts +1 -1
- package/package.json +4 -2
- package/src/adapters/AdapterFactory.ts +4 -4
- package/src/adapters/AlipayAdapter.ts +76 -17
- package/src/adapters/BaiduAdapter.ts +74 -17
- package/src/adapters/BaseAdapter.ts +9 -15
- package/src/adapters/ByteDanceAdapter.ts +78 -17
- package/src/adapters/TaroAdapter.ts +69 -12
- package/src/core/BluetoothPrinter.ts +150 -158
- package/src/core/EventEmitter.ts +114 -8
- package/src/drivers/EscPos.ts +4 -4
- package/src/services/CommandBuilder.ts +135 -0
- package/src/services/ConnectionManager.ts +133 -0
- package/src/services/PrintJobManager.ts +300 -0
- package/src/services/interfaces/index.ts +188 -0
- package/src/types.ts +10 -10
- package/src/utils/encoding.ts +34 -4
- package/src/utils/image.ts +9 -7
- package/src/utils/logger.ts +143 -37
- package/src/utils/platform.ts +27 -13
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Builder Service
|
|
3
|
+
*
|
|
4
|
+
* Builds print commands using the printer driver
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Service } from 'typedi';
|
|
8
|
+
import type { IPrinterDriver, IQrOptions } from '@/types';
|
|
9
|
+
import { ICommandBuilder } from './interfaces';
|
|
10
|
+
import { Logger } from '@/utils/logger';
|
|
11
|
+
import { EscPos } from '@/drivers/EscPos';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Command Builder implementation
|
|
15
|
+
*/
|
|
16
|
+
@Service()
|
|
17
|
+
export class CommandBuilder implements ICommandBuilder {
|
|
18
|
+
private driver: IPrinterDriver;
|
|
19
|
+
private buffer: Uint8Array[] = [];
|
|
20
|
+
private readonly logger = Logger.scope('CommandBuilder');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new CommandBuilder instance
|
|
24
|
+
*
|
|
25
|
+
* @param driver - Printer driver instance
|
|
26
|
+
*/
|
|
27
|
+
constructor(driver?: IPrinterDriver) {
|
|
28
|
+
this.driver = driver || new EscPos();
|
|
29
|
+
|
|
30
|
+
// Initialize printer with ESC/POS init command
|
|
31
|
+
this.buffer.push(...this.driver.init());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adds text to the print queue
|
|
36
|
+
*
|
|
37
|
+
* @param content - Text content
|
|
38
|
+
* @param encoding - Text encoding
|
|
39
|
+
* @returns this - For method chaining
|
|
40
|
+
*/
|
|
41
|
+
text(content: string, encoding?: string): this {
|
|
42
|
+
this.logger.debug('Adding text:', content.substring(0, 50));
|
|
43
|
+
this.buffer.push(...this.driver.text(content, encoding));
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Adds line feeds to the print queue
|
|
49
|
+
*
|
|
50
|
+
* @param lines - Number of lines to feed
|
|
51
|
+
* @returns this - For method chaining
|
|
52
|
+
*/
|
|
53
|
+
feed(lines = 1): this {
|
|
54
|
+
this.logger.debug('Adding feed:', lines);
|
|
55
|
+
this.buffer.push(...this.driver.feed(lines));
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adds a paper cut command to the print queue
|
|
61
|
+
*
|
|
62
|
+
* @returns this - For method chaining
|
|
63
|
+
*/
|
|
64
|
+
cut(): this {
|
|
65
|
+
this.logger.debug('Adding cut command');
|
|
66
|
+
this.buffer.push(...this.driver.cut());
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Adds an image to the print queue
|
|
72
|
+
*
|
|
73
|
+
* @param data - Image data as Uint8Array
|
|
74
|
+
* @param width - Image width
|
|
75
|
+
* @param height - Image height
|
|
76
|
+
* @returns this - For method chaining
|
|
77
|
+
*/
|
|
78
|
+
image(data: Uint8Array, width: number, height: number): this {
|
|
79
|
+
this.logger.debug(`Adding image: ${width}x${height}`);
|
|
80
|
+
this.buffer.push(...this.driver.image(data, width, height));
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Adds a QR code to the print queue
|
|
86
|
+
*
|
|
87
|
+
* @param content - QR code content
|
|
88
|
+
* @param options - QR code options
|
|
89
|
+
* @returns this - For method chaining
|
|
90
|
+
*/
|
|
91
|
+
qr(content: string, options?: IQrOptions): this {
|
|
92
|
+
this.logger.debug('Adding QR code:', content.substring(0, 50));
|
|
93
|
+
this.buffer.push(...this.driver.qr(content, options));
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clears the print queue
|
|
99
|
+
*
|
|
100
|
+
* @returns this - For method chaining
|
|
101
|
+
*/
|
|
102
|
+
clear(): this {
|
|
103
|
+
this.logger.debug('Clearing buffer');
|
|
104
|
+
this.buffer = [];
|
|
105
|
+
// Re-initialize printer
|
|
106
|
+
this.buffer.push(...this.driver.init());
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets the current buffer
|
|
112
|
+
*
|
|
113
|
+
* @returns Uint8Array - Current print buffer
|
|
114
|
+
*/
|
|
115
|
+
getBuffer(): Uint8Array {
|
|
116
|
+
// Combine all buffers
|
|
117
|
+
const totalLength = this.buffer.reduce((acc, b) => acc + b.length, 0);
|
|
118
|
+
const combined = new Uint8Array(totalLength);
|
|
119
|
+
let offset = 0;
|
|
120
|
+
for (const b of this.buffer) {
|
|
121
|
+
combined.set(b, offset);
|
|
122
|
+
offset += b.length;
|
|
123
|
+
}
|
|
124
|
+
return combined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Gets the total number of bytes in the buffer
|
|
129
|
+
*
|
|
130
|
+
* @returns number - Total bytes
|
|
131
|
+
*/
|
|
132
|
+
getTotalBytes(): number {
|
|
133
|
+
return this.buffer.reduce((acc, b) => acc + b.length, 0);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Manages Bluetooth device connections
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Service } from 'typedi';
|
|
8
|
+
import type { IPrinterAdapter } from '@/types';
|
|
9
|
+
import { PrinterState } from '@/types';
|
|
10
|
+
import { IConnectionManager } from './interfaces';
|
|
11
|
+
import { AdapterFactory } from '@/adapters/AdapterFactory';
|
|
12
|
+
import { Logger } from '@/utils/logger';
|
|
13
|
+
import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Connection Manager implementation
|
|
17
|
+
*/
|
|
18
|
+
@Service()
|
|
19
|
+
export class ConnectionManager implements IConnectionManager {
|
|
20
|
+
private adapter: IPrinterAdapter;
|
|
21
|
+
private deviceId: string | null = null;
|
|
22
|
+
private state: PrinterState = PrinterState.DISCONNECTED;
|
|
23
|
+
private readonly logger = Logger.scope('ConnectionManager');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new ConnectionManager instance
|
|
27
|
+
*/
|
|
28
|
+
constructor(adapter?: IPrinterAdapter) {
|
|
29
|
+
this.adapter = adapter || AdapterFactory.create();
|
|
30
|
+
|
|
31
|
+
// Listen to adapter state changes
|
|
32
|
+
this.adapter.onStateChange?.(state => {
|
|
33
|
+
this.state = state;
|
|
34
|
+
this.logger.debug('State changed:', state);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Connects to a Bluetooth device
|
|
40
|
+
*
|
|
41
|
+
* @param deviceId - Bluetooth device ID
|
|
42
|
+
* @returns Promise<void>
|
|
43
|
+
*/
|
|
44
|
+
async connect(deviceId: string): Promise<void> {
|
|
45
|
+
this.logger.info('Connecting to device:', deviceId);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
this.deviceId = deviceId;
|
|
49
|
+
await this.adapter.connect(deviceId);
|
|
50
|
+
this.state = PrinterState.CONNECTED;
|
|
51
|
+
this.logger.info('Connected successfully');
|
|
52
|
+
} catch (error) {
|
|
53
|
+
this.deviceId = null;
|
|
54
|
+
this.state = PrinterState.DISCONNECTED;
|
|
55
|
+
const printError =
|
|
56
|
+
error instanceof BluetoothPrintError
|
|
57
|
+
? error
|
|
58
|
+
: new BluetoothPrintError(
|
|
59
|
+
ErrorCode.CONNECTION_FAILED,
|
|
60
|
+
'Connection failed',
|
|
61
|
+
error as Error
|
|
62
|
+
);
|
|
63
|
+
this.logger.error('Connection failed:', printError);
|
|
64
|
+
throw printError;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Disconnects from the current device
|
|
70
|
+
*
|
|
71
|
+
* @returns Promise<void>
|
|
72
|
+
*/
|
|
73
|
+
async disconnect(): Promise<void> {
|
|
74
|
+
if (!this.deviceId) {
|
|
75
|
+
this.logger.warn('Disconnect called but no device connected');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const deviceId = this.deviceId;
|
|
80
|
+
this.logger.info('Disconnecting from device:', deviceId);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await this.adapter.disconnect(deviceId);
|
|
84
|
+
this.deviceId = null;
|
|
85
|
+
this.state = PrinterState.DISCONNECTED;
|
|
86
|
+
this.logger.info('Disconnected successfully');
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const printError = new BluetoothPrintError(
|
|
89
|
+
ErrorCode.DEVICE_DISCONNECTED,
|
|
90
|
+
'Disconnect failed',
|
|
91
|
+
error as Error
|
|
92
|
+
);
|
|
93
|
+
this.logger.error('Disconnect failed:', printError);
|
|
94
|
+
throw printError;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Checks if a device is connected
|
|
100
|
+
*
|
|
101
|
+
* @returns boolean - True if connected, false otherwise
|
|
102
|
+
*/
|
|
103
|
+
isConnected(): boolean {
|
|
104
|
+
return this.state === PrinterState.CONNECTED;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Gets the current device ID
|
|
109
|
+
*
|
|
110
|
+
* @returns string | null - Device ID or null if not connected
|
|
111
|
+
*/
|
|
112
|
+
getDeviceId(): string | null {
|
|
113
|
+
return this.deviceId;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Gets the current connection state
|
|
118
|
+
*
|
|
119
|
+
* @returns PrinterState - Current state
|
|
120
|
+
*/
|
|
121
|
+
getState(): PrinterState {
|
|
122
|
+
return this.state;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Gets the printer adapter instance
|
|
127
|
+
*
|
|
128
|
+
* @returns IPrinterAdapter - Printer adapter
|
|
129
|
+
*/
|
|
130
|
+
getAdapter(): IPrinterAdapter {
|
|
131
|
+
return this.adapter;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Print Job Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Manages print jobs, including pause/resume/cancel functionality
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Service } from 'typedi';
|
|
8
|
+
import type { IPrinterAdapter } from '@/types';
|
|
9
|
+
import { IAdapterOptions, PrinterState } from '@/types';
|
|
10
|
+
import { IPrintJobManager, IConnectionManager } from './interfaces';
|
|
11
|
+
import { Logger } from '@/utils/logger';
|
|
12
|
+
import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Print Job Manager implementation
|
|
16
|
+
*/
|
|
17
|
+
@Service()
|
|
18
|
+
export class PrintJobManager implements IPrintJobManager {
|
|
19
|
+
private adapter: IPrinterAdapter;
|
|
20
|
+
private connectionManager: IConnectionManager;
|
|
21
|
+
private jobBuffer: Uint8Array | null = null;
|
|
22
|
+
private jobOffset = 0;
|
|
23
|
+
private _isPaused = false;
|
|
24
|
+
private _isInProgress = false;
|
|
25
|
+
private adapterOptions: IAdapterOptions = {};
|
|
26
|
+
private readonly logger = Logger.scope('PrintJobManager');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new PrintJobManager instance
|
|
30
|
+
*
|
|
31
|
+
* @param connectionManagerOrAdapter - Connection manager instance or adapter instance (for backward compatibility)
|
|
32
|
+
* @param adapter - Printer adapter instance (optional, will be taken from connectionManager if not provided)
|
|
33
|
+
*/
|
|
34
|
+
constructor(
|
|
35
|
+
connectionManagerOrAdapter?: IConnectionManager | IPrinterAdapter,
|
|
36
|
+
adapter?: IPrinterAdapter
|
|
37
|
+
) {
|
|
38
|
+
// Handle case where adapter is explicitly provided
|
|
39
|
+
if (adapter) {
|
|
40
|
+
// New API: new PrintJobManager(connectionManager, adapter)
|
|
41
|
+
this.connectionManager = connectionManagerOrAdapter as IConnectionManager;
|
|
42
|
+
this.adapter = adapter;
|
|
43
|
+
}
|
|
44
|
+
// Handle backward compatibility for old API: new PrintJobManager(adapter, driver)
|
|
45
|
+
else if (
|
|
46
|
+
connectionManagerOrAdapter &&
|
|
47
|
+
typeof (connectionManagerOrAdapter as IPrinterAdapter).connect === 'function'
|
|
48
|
+
) {
|
|
49
|
+
// Old API: new PrintJobManager(adapter)
|
|
50
|
+
this.adapter = connectionManagerOrAdapter as IPrinterAdapter;
|
|
51
|
+
// Create a minimal connection manager for backward compatibility
|
|
52
|
+
this.connectionManager = {
|
|
53
|
+
getDeviceId: () => 'test-device', // Default device ID for backward compatibility
|
|
54
|
+
isConnected: () => true,
|
|
55
|
+
getState: () => PrinterState.CONNECTED, // Default to CONNECTED
|
|
56
|
+
connect: () => Promise.resolve(),
|
|
57
|
+
disconnect: () => Promise.resolve(),
|
|
58
|
+
getAdapter: () => this.adapter,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Handle new API: new PrintJobManager(connectionManager)
|
|
62
|
+
else {
|
|
63
|
+
// New API: dependency injection or manual service creation
|
|
64
|
+
this.connectionManager = connectionManagerOrAdapter as IConnectionManager;
|
|
65
|
+
// Try to get adapter from connectionManager
|
|
66
|
+
if (
|
|
67
|
+
this.connectionManager &&
|
|
68
|
+
typeof (
|
|
69
|
+
this.connectionManager as IConnectionManager & { getAdapter?: () => IPrinterAdapter }
|
|
70
|
+
).getAdapter === 'function'
|
|
71
|
+
) {
|
|
72
|
+
this.adapter = (
|
|
73
|
+
this.connectionManager as IConnectionManager & { getAdapter: () => IPrinterAdapter }
|
|
74
|
+
).getAdapter();
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'Printer adapter not provided and could not be retrieved from connection manager'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Starts a print job
|
|
85
|
+
*
|
|
86
|
+
* @param buffer - Print data buffer
|
|
87
|
+
* @returns Promise<void>
|
|
88
|
+
*/
|
|
89
|
+
async start(buffer: Uint8Array): Promise<void> {
|
|
90
|
+
if (this._isInProgress) {
|
|
91
|
+
throw new BluetoothPrintError(
|
|
92
|
+
ErrorCode.PRINT_JOB_IN_PROGRESS,
|
|
93
|
+
'A print job is already in progress. Wait for completion or cancel it.'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.logger.info(`Starting print job: ${buffer.length} bytes`);
|
|
98
|
+
|
|
99
|
+
this.jobBuffer = buffer;
|
|
100
|
+
this.jobOffset = 0;
|
|
101
|
+
this._isPaused = false;
|
|
102
|
+
this._isInProgress = true;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await this.processJob();
|
|
106
|
+
|
|
107
|
+
// Check if the job was paused
|
|
108
|
+
if (this._isPaused) {
|
|
109
|
+
this.logger.info('Print job paused');
|
|
110
|
+
// Don't reset _isInProgress when paused, so resume() and cancel() can still work
|
|
111
|
+
} else {
|
|
112
|
+
// Print job completed successfully
|
|
113
|
+
this.logger.info('Print job completed successfully');
|
|
114
|
+
this._isInProgress = false;
|
|
115
|
+
this.jobBuffer = null;
|
|
116
|
+
this.jobOffset = 0;
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.logger.error('Print job failed:', error);
|
|
120
|
+
this._isInProgress = false;
|
|
121
|
+
this.jobBuffer = null;
|
|
122
|
+
this.jobOffset = 0;
|
|
123
|
+
|
|
124
|
+
const printError =
|
|
125
|
+
error instanceof BluetoothPrintError
|
|
126
|
+
? error
|
|
127
|
+
: new BluetoothPrintError(ErrorCode.PRINT_JOB_FAILED, 'Print job failed', error as Error);
|
|
128
|
+
throw printError;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Pauses the current print job
|
|
134
|
+
*/
|
|
135
|
+
pause(): void {
|
|
136
|
+
if (!this._isInProgress) {
|
|
137
|
+
this.logger.warn('Pause called but no print job in progress');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this._isPaused = true;
|
|
142
|
+
this.logger.info('Print job paused');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Resumes a paused print job
|
|
147
|
+
*
|
|
148
|
+
* @returns Promise<void>
|
|
149
|
+
*/
|
|
150
|
+
async resume(): Promise<void> {
|
|
151
|
+
if (!this._isInProgress || !this._isPaused) {
|
|
152
|
+
this.logger.warn('Resume called but no paused print job');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this._isPaused = false;
|
|
157
|
+
this.logger.info('Print job resumed');
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
await this.processJob();
|
|
161
|
+
|
|
162
|
+
if (!this._isPaused) {
|
|
163
|
+
// Print job completed successfully
|
|
164
|
+
this.logger.info('Print job completed successfully');
|
|
165
|
+
this._isInProgress = false;
|
|
166
|
+
this.jobBuffer = null;
|
|
167
|
+
this.jobOffset = 0;
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
this.logger.error('Print job failed after resume:', error);
|
|
171
|
+
this._isInProgress = false;
|
|
172
|
+
this.jobBuffer = null;
|
|
173
|
+
this.jobOffset = 0;
|
|
174
|
+
|
|
175
|
+
const printError =
|
|
176
|
+
error instanceof BluetoothPrintError
|
|
177
|
+
? error
|
|
178
|
+
: new BluetoothPrintError(ErrorCode.PRINT_JOB_FAILED, 'Print job failed', error as Error);
|
|
179
|
+
throw printError;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Cancels the current print job
|
|
185
|
+
*/
|
|
186
|
+
cancel(): void {
|
|
187
|
+
if (!this._isInProgress) {
|
|
188
|
+
this.logger.warn('Cancel called but no print job in progress');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this._isPaused = false;
|
|
193
|
+
this._isInProgress = false;
|
|
194
|
+
this.jobBuffer = null;
|
|
195
|
+
this.jobOffset = 0;
|
|
196
|
+
this.logger.info('Print job cancelled');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Gets the number of bytes remaining to print
|
|
201
|
+
*
|
|
202
|
+
* @returns number - Bytes remaining
|
|
203
|
+
*/
|
|
204
|
+
remaining(): number {
|
|
205
|
+
if (this.jobBuffer) {
|
|
206
|
+
return this.jobBuffer.length - this.jobOffset;
|
|
207
|
+
}
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Checks if the print job is paused
|
|
213
|
+
*
|
|
214
|
+
* @returns boolean - True if paused, false otherwise
|
|
215
|
+
*/
|
|
216
|
+
isPaused(): boolean {
|
|
217
|
+
return this._isPaused;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Checks if a print job is in progress
|
|
222
|
+
*
|
|
223
|
+
* @returns boolean - True if in progress, false otherwise
|
|
224
|
+
*/
|
|
225
|
+
isInProgress(): boolean {
|
|
226
|
+
return this._isInProgress;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Sets adapter options for write operations
|
|
231
|
+
*
|
|
232
|
+
* @param options - Adapter options
|
|
233
|
+
*/
|
|
234
|
+
setOptions(options: IAdapterOptions): void {
|
|
235
|
+
this.adapterOptions = { ...this.adapterOptions, ...options };
|
|
236
|
+
this.logger.debug('Adapter options updated:', this.adapterOptions);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Processes the print job in chunks
|
|
241
|
+
* Supports pause/resume functionality
|
|
242
|
+
*
|
|
243
|
+
* @returns Promise<void>
|
|
244
|
+
*/
|
|
245
|
+
private async processJob(): Promise<void> {
|
|
246
|
+
if (!this.jobBuffer) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check if adapter has write method
|
|
251
|
+
if (!this.adapter || typeof this.adapter.write !== 'function') {
|
|
252
|
+
throw new BluetoothPrintError(
|
|
253
|
+
ErrorCode.INVALID_CONFIGURATION,
|
|
254
|
+
'Printer adapter does not support write operation'
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const printerChunkSize = 512;
|
|
259
|
+
const total = this.jobBuffer.length;
|
|
260
|
+
const jobBuffer = this.jobBuffer; // Cache to avoid closure issues
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
while (this.jobOffset < jobBuffer.length) {
|
|
264
|
+
if (this._isPaused) {
|
|
265
|
+
this.logger.debug('Job paused at offset:', this.jobOffset);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check device connection state
|
|
270
|
+
// Note: We rely on the adapter to handle connection state changes
|
|
271
|
+
|
|
272
|
+
const end = Math.min(this.jobOffset + printerChunkSize, jobBuffer.length);
|
|
273
|
+
const chunk = jobBuffer.slice(this.jobOffset, end);
|
|
274
|
+
|
|
275
|
+
await this.adapter.write(this.getDeviceId(), chunk.buffer, this.adapterOptions);
|
|
276
|
+
|
|
277
|
+
this.jobOffset = end;
|
|
278
|
+
|
|
279
|
+
this.logger.debug(`Processed ${this.jobOffset}/${total} bytes`);
|
|
280
|
+
}
|
|
281
|
+
} catch (error) {
|
|
282
|
+
this.logger.error('Error processing job:', error);
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Gets the current device ID from the connection manager
|
|
289
|
+
*
|
|
290
|
+
* @returns string - Device ID
|
|
291
|
+
* @throws BluetoothPrintError if no device is connected
|
|
292
|
+
*/
|
|
293
|
+
private getDeviceId(): string {
|
|
294
|
+
const deviceId = this.connectionManager.getDeviceId();
|
|
295
|
+
if (!deviceId) {
|
|
296
|
+
throw new BluetoothPrintError(ErrorCode.DEVICE_DISCONNECTED, 'Device ID not available');
|
|
297
|
+
}
|
|
298
|
+
return deviceId;
|
|
299
|
+
}
|
|
300
|
+
}
|