taro-bluetooth-print 2.3.0 → 2.4.0
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 +73 -195
- package/README.md +134 -386
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/config/PrinterConfigManager.d.ts +206 -0
- package/dist/types/config/index.d.ts +8 -0
- package/dist/types/core/BluetoothPrinter.d.ts +1 -1
- package/dist/types/core/EventEmitter.d.ts +6 -26
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/device/MultiPrinterManager.d.ts +164 -0
- package/dist/types/device/index.d.ts +2 -0
- package/dist/types/drivers/CpclDriver.d.ts +304 -0
- package/dist/types/drivers/GPrinterDriver.d.ts +63 -0
- package/dist/types/drivers/ZplDriver.d.ts +325 -0
- package/dist/types/drivers/index.d.ts +9 -0
- package/dist/types/encoding/gbk-lite.d.ts +8 -0
- package/dist/types/encoding/gbk-table.d.ts +8 -30
- package/dist/types/index.d.ts +12 -8
- package/dist/types/services/BatchPrintManager.d.ts +205 -0
- package/dist/types/services/ConnectionManager.d.ts +1 -1
- package/dist/types/services/PrintHistory.d.ts +142 -0
- package/dist/types/services/PrintJobManager.d.ts +28 -4
- package/dist/types/services/PrinterStatus.d.ts +97 -0
- package/dist/types/services/index.d.ts +11 -0
- package/package.json +25 -7
- package/src/adapters/AlipayAdapter.ts +1 -0
- package/src/adapters/BaiduAdapter.ts +1 -0
- package/src/adapters/BaseAdapter.ts +6 -8
- package/src/adapters/ByteDanceAdapter.ts +1 -0
- package/src/adapters/TaroAdapter.ts +1 -0
- package/src/adapters/WebBluetoothAdapter.ts +1 -1
- package/src/config/PrinterConfigManager.ts +519 -0
- package/src/config/index.ts +15 -0
- package/src/core/BluetoothPrinter.ts +15 -15
- package/src/core/EventEmitter.ts +15 -15
- package/src/core/index.ts +7 -0
- package/src/device/MultiPrinterManager.ts +470 -0
- package/src/device/index.ts +8 -0
- package/src/drivers/CpclDriver.ts +549 -0
- package/src/drivers/GPrinterDriver.ts +115 -0
- package/src/drivers/TsplDriver.ts +9 -21
- package/src/drivers/ZplDriver.ts +543 -0
- package/src/drivers/index.ts +37 -0
- package/src/encoding/gbk-lite.ts +113 -0
- package/src/encoding/gbk-table.ts +80 -58
- package/src/index.ts +40 -35
- package/src/plugins/PluginManager.ts +3 -1
- package/src/plugins/builtin/LoggingPlugin.ts +4 -2
- package/src/plugins/builtin/RetryPlugin.ts +8 -14
- package/src/services/BatchPrintManager.ts +500 -0
- package/src/services/ConnectionManager.ts +25 -22
- package/src/services/PrintHistory.ts +336 -0
- package/src/services/PrintJobManager.ts +69 -9
- package/src/services/PrinterStatus.ts +267 -0
- package/src/services/index.ts +22 -0
- package/src/template/TemplateEngine.ts +4 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
|
|
7
7
|
|
|
8
8
|
// Declare Baidu global for TypeScript
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
9
10
|
interface BaiduGlobal extends MiniProgramBLEApi {}
|
|
10
11
|
|
|
11
12
|
declare const swan: BaiduGlobal;
|
|
@@ -242,15 +242,13 @@ export abstract class MiniProgramAdapter extends BaseAdapter {
|
|
|
242
242
|
this.logger.info('Device connected successfully');
|
|
243
243
|
|
|
244
244
|
// Listen for connection state changes
|
|
245
|
-
this.getApi().onBLEConnectionStateChange(
|
|
246
|
-
(res
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.cleanupDevice(deviceId);
|
|
251
|
-
}
|
|
245
|
+
this.getApi().onBLEConnectionStateChange((res: { deviceId: string; connected: boolean }) => {
|
|
246
|
+
if (res.deviceId === deviceId && !res.connected) {
|
|
247
|
+
this.logger.warn('Device disconnected unexpectedly');
|
|
248
|
+
this.updateState(PrinterState.DISCONNECTED);
|
|
249
|
+
this.cleanupDevice(deviceId);
|
|
252
250
|
}
|
|
253
|
-
);
|
|
251
|
+
});
|
|
254
252
|
} catch (error) {
|
|
255
253
|
this.updateState(PrinterState.DISCONNECTED);
|
|
256
254
|
this.logger.error('Connection failed:', error);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
|
|
7
7
|
|
|
8
8
|
// Declare ByteDance global for TypeScript
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
9
10
|
interface ByteDanceGlobal extends MiniProgramBLEApi {}
|
|
10
11
|
|
|
11
12
|
declare const tt: ByteDanceGlobal;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
|
|
7
7
|
|
|
8
8
|
// Declare Taro global for TypeScript
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
9
10
|
interface TaroGlobal extends MiniProgramBLEApi {}
|
|
10
11
|
|
|
11
12
|
declare const Taro: TaroGlobal;
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Printer Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages persistent printer configurations including:
|
|
5
|
+
* - Saved devices and their settings
|
|
6
|
+
* - Default print parameters
|
|
7
|
+
* - User preferences
|
|
8
|
+
*
|
|
9
|
+
* Supports localStorage and file-based storage backends.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const configManager = new PrinterConfigManager();
|
|
14
|
+
*
|
|
15
|
+
* // Save a printer configuration
|
|
16
|
+
* configManager.savePrinter({
|
|
17
|
+
* id: 'my-printer',
|
|
18
|
+
* deviceId: 'XX:XX:XX:XX:XX:XX',
|
|
19
|
+
* name: 'Kitchen Printer',
|
|
20
|
+
* isDefault: true
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Get all saved printers
|
|
24
|
+
* const printers = configManager.getSavedPrinters();
|
|
25
|
+
*
|
|
26
|
+
* // Load configuration for a printer
|
|
27
|
+
* const config = configManager.loadPrinterConfig('my-printer');
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { Logger } from '@/utils/logger';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Print configuration for a device
|
|
35
|
+
*/
|
|
36
|
+
export interface PrintConfig {
|
|
37
|
+
/** Encoding (default: 'GBK') */
|
|
38
|
+
encoding?: string;
|
|
39
|
+
/** Chunk size for data transfer */
|
|
40
|
+
chunkSize?: number;
|
|
41
|
+
/** Delay between chunks in ms */
|
|
42
|
+
chunkDelay?: number;
|
|
43
|
+
/** Number of retries */
|
|
44
|
+
retries?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Saved printer configuration
|
|
49
|
+
*/
|
|
50
|
+
export interface SavedPrinter {
|
|
51
|
+
/** Unique printer ID */
|
|
52
|
+
id: string;
|
|
53
|
+
/** Bluetooth device ID */
|
|
54
|
+
deviceId: string;
|
|
55
|
+
/** Friendly name */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Printer model/type */
|
|
58
|
+
model?: string;
|
|
59
|
+
/** Whether this is the default printer */
|
|
60
|
+
isDefault?: boolean;
|
|
61
|
+
/** Print configuration */
|
|
62
|
+
printConfig?: PrintConfig;
|
|
63
|
+
/** Auto-reconnect on disconnect */
|
|
64
|
+
autoReconnect?: boolean;
|
|
65
|
+
/** Last connected timestamp */
|
|
66
|
+
lastConnected?: number;
|
|
67
|
+
/** Creation timestamp */
|
|
68
|
+
createdAt: number;
|
|
69
|
+
/** Update timestamp */
|
|
70
|
+
updatedAt: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Global configuration
|
|
75
|
+
*/
|
|
76
|
+
export interface GlobalConfig {
|
|
77
|
+
/** Default encoding */
|
|
78
|
+
defaultEncoding: string;
|
|
79
|
+
/** Default chunk size */
|
|
80
|
+
defaultChunkSize: number;
|
|
81
|
+
/** Default chunk delay */
|
|
82
|
+
defaultChunkDelay: number;
|
|
83
|
+
/** Default retries */
|
|
84
|
+
defaultRetries: number;
|
|
85
|
+
/** Auto-reconnect by default */
|
|
86
|
+
defaultAutoReconnect: boolean;
|
|
87
|
+
/** Scan timeout in ms */
|
|
88
|
+
scanTimeout: number;
|
|
89
|
+
/** Enable logging */
|
|
90
|
+
enableLogging: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Configuration storage interface
|
|
95
|
+
*/
|
|
96
|
+
export interface IConfigStorage {
|
|
97
|
+
get<T>(key: string, defaultValue: T): T;
|
|
98
|
+
set<T>(key: string, value: T): void;
|
|
99
|
+
remove(key: string): void;
|
|
100
|
+
clear(): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* LocalStorage-based implementation
|
|
105
|
+
*/
|
|
106
|
+
export class LocalStorage implements IConfigStorage {
|
|
107
|
+
private prefix: string;
|
|
108
|
+
|
|
109
|
+
constructor(prefix = 'taro_bt_print_') {
|
|
110
|
+
this.prefix = prefix;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get<T>(key: string, defaultValue: T): T {
|
|
114
|
+
try {
|
|
115
|
+
const stored = localStorage.getItem(this.prefix + key);
|
|
116
|
+
if (stored === null) {
|
|
117
|
+
return defaultValue;
|
|
118
|
+
}
|
|
119
|
+
return JSON.parse(stored) as T;
|
|
120
|
+
} catch {
|
|
121
|
+
return defaultValue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
set<T>(key: string, value: T): void {
|
|
126
|
+
try {
|
|
127
|
+
localStorage.setItem(this.prefix + key, JSON.stringify(value));
|
|
128
|
+
} catch (error) {
|
|
129
|
+
Logger.scope('ConfigStorage').error(`Failed to save "${key}":`, error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
remove(key: string): void {
|
|
134
|
+
try {
|
|
135
|
+
localStorage.removeItem(this.prefix + key);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
Logger.scope('ConfigStorage').error(`Failed to remove "${key}":`, error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
clear(): void {
|
|
142
|
+
try {
|
|
143
|
+
const keysToRemove: string[] = [];
|
|
144
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
145
|
+
const key = localStorage.key(i);
|
|
146
|
+
if (key?.startsWith(this.prefix)) {
|
|
147
|
+
keysToRemove.push(key);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
151
|
+
} catch (error) {
|
|
152
|
+
Logger.scope('ConfigStorage').error('Failed to clear storage:', error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Default global configuration
|
|
159
|
+
*/
|
|
160
|
+
const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
|
|
161
|
+
defaultEncoding: 'GBK',
|
|
162
|
+
defaultChunkSize: 20,
|
|
163
|
+
defaultChunkDelay: 20,
|
|
164
|
+
defaultRetries: 3,
|
|
165
|
+
defaultAutoReconnect: true,
|
|
166
|
+
scanTimeout: 15000,
|
|
167
|
+
enableLogging: true,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Storage keys
|
|
172
|
+
*/
|
|
173
|
+
const KEYS = {
|
|
174
|
+
PRINTERS: 'printers',
|
|
175
|
+
GLOBAL_CONFIG: 'global_config',
|
|
176
|
+
LAST_USED_PRINTER: 'last_used_printer',
|
|
177
|
+
} as const;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Printer Configuration Manager
|
|
181
|
+
*/
|
|
182
|
+
export class PrinterConfigManager {
|
|
183
|
+
private readonly logger = Logger.scope('PrinterConfigManager');
|
|
184
|
+
private readonly storage: IConfigStorage;
|
|
185
|
+
private printers: Map<string, SavedPrinter> = new Map();
|
|
186
|
+
private globalConfig: GlobalConfig;
|
|
187
|
+
private lastUsedPrinterId: string | null = null;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Creates a new PrinterConfigManager instance
|
|
191
|
+
*
|
|
192
|
+
* @param storage - Storage backend (defaults to LocalStorage)
|
|
193
|
+
*/
|
|
194
|
+
constructor(storage?: IConfigStorage) {
|
|
195
|
+
this.storage = storage || new LocalStorage();
|
|
196
|
+
this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG };
|
|
197
|
+
this.load();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Load configuration from storage
|
|
202
|
+
*/
|
|
203
|
+
private load(): void {
|
|
204
|
+
try {
|
|
205
|
+
// Load printers
|
|
206
|
+
const printers = this.storage.get<SavedPrinter[]>(KEYS.PRINTERS, []);
|
|
207
|
+
this.printers.clear();
|
|
208
|
+
for (const printer of printers) {
|
|
209
|
+
if (printer.id) {
|
|
210
|
+
this.printers.set(printer.id, printer);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Load global config
|
|
215
|
+
const globalConfig = this.storage.get<GlobalConfig | null>(KEYS.GLOBAL_CONFIG, null);
|
|
216
|
+
if (globalConfig) {
|
|
217
|
+
this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG, ...globalConfig };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Load last used
|
|
221
|
+
this.lastUsedPrinterId = this.storage.get<string | null>(KEYS.LAST_USED_PRINTER, null);
|
|
222
|
+
|
|
223
|
+
this.logger.debug(`Loaded ${this.printers.size} saved printers`);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
this.logger.error('Failed to load configuration:', error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Save configuration to storage
|
|
231
|
+
*/
|
|
232
|
+
private save(): void {
|
|
233
|
+
try {
|
|
234
|
+
// Save printers
|
|
235
|
+
const printersArray = Array.from(this.printers.values());
|
|
236
|
+
this.storage.set(KEYS.PRINTERS, printersArray);
|
|
237
|
+
|
|
238
|
+
// Save global config
|
|
239
|
+
this.storage.set(KEYS.GLOBAL_CONFIG, this.globalConfig);
|
|
240
|
+
|
|
241
|
+
// Save last used
|
|
242
|
+
if (this.lastUsedPrinterId) {
|
|
243
|
+
this.storage.set(KEYS.LAST_USED_PRINTER, this.lastUsedPrinterId);
|
|
244
|
+
} else {
|
|
245
|
+
this.storage.remove(KEYS.LAST_USED_PRINTER);
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this.logger.error('Failed to save configuration:', error);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Save a printer configuration
|
|
254
|
+
*
|
|
255
|
+
* @param printer - Printer configuration to save
|
|
256
|
+
* @returns The saved printer ID
|
|
257
|
+
*/
|
|
258
|
+
savePrinter(printer: SavedPrinter): string {
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
const existing = this.printers.get(printer.id);
|
|
261
|
+
|
|
262
|
+
const savedPrinter: SavedPrinter = {
|
|
263
|
+
...printer,
|
|
264
|
+
id: printer.id,
|
|
265
|
+
createdAt: existing?.createdAt ?? now,
|
|
266
|
+
updatedAt: now,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// If setting as default, unset other defaults
|
|
270
|
+
if (savedPrinter.isDefault) {
|
|
271
|
+
for (const [id, p] of this.printers.entries()) {
|
|
272
|
+
if (id !== printer.id && p.isDefault) {
|
|
273
|
+
this.printers.set(id, { ...p, isDefault: false });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.printers.set(printer.id, savedPrinter);
|
|
279
|
+
this.save();
|
|
280
|
+
|
|
281
|
+
this.logger.info(`Saved printer: ${printer.id} (${printer.name})`);
|
|
282
|
+
return printer.id;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get a saved printer by ID
|
|
287
|
+
*/
|
|
288
|
+
getPrinter(id: string): SavedPrinter | undefined {
|
|
289
|
+
return this.printers.get(id);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get all saved printers
|
|
294
|
+
*/
|
|
295
|
+
getSavedPrinters(): SavedPrinter[] {
|
|
296
|
+
return Array.from(this.printers.values());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get default printer
|
|
301
|
+
*/
|
|
302
|
+
getDefaultPrinter(): SavedPrinter | undefined {
|
|
303
|
+
for (const printer of this.printers.values()) {
|
|
304
|
+
if (printer.isDefault) {
|
|
305
|
+
return printer;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Fall back to last used
|
|
310
|
+
if (this.lastUsedPrinterId) {
|
|
311
|
+
return this.printers.get(this.lastUsedPrinterId);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Remove a saved printer
|
|
319
|
+
*/
|
|
320
|
+
removePrinter(id: string): boolean {
|
|
321
|
+
const deleted = this.printers.delete(id);
|
|
322
|
+
if (deleted) {
|
|
323
|
+
this.save();
|
|
324
|
+
this.logger.info(`Removed printer: ${id}`);
|
|
325
|
+
|
|
326
|
+
if (this.lastUsedPrinterId === id) {
|
|
327
|
+
this.lastUsedPrinterId = null;
|
|
328
|
+
this.save();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return deleted;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Set printer as default
|
|
336
|
+
*/
|
|
337
|
+
setDefaultPrinter(id: string): void {
|
|
338
|
+
const printer = this.printers.get(id);
|
|
339
|
+
if (!printer) {
|
|
340
|
+
throw new Error(`Printer not found: ${id}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Unset other defaults
|
|
344
|
+
for (const [pid, p] of this.printers.entries()) {
|
|
345
|
+
if (p.isDefault && pid !== id) {
|
|
346
|
+
this.printers.set(pid, { ...p, isDefault: false });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Set this one as default
|
|
351
|
+
this.printers.set(id, { ...printer, isDefault: true });
|
|
352
|
+
this.save();
|
|
353
|
+
|
|
354
|
+
this.logger.info(`Set default printer: ${id}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Update last used printer
|
|
359
|
+
*/
|
|
360
|
+
setLastUsed(id: string): void {
|
|
361
|
+
if (!this.printers.has(id)) {
|
|
362
|
+
this.logger.warn(`Cannot set last used: printer not found ${id}`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
this.lastUsedPrinterId = id;
|
|
367
|
+
|
|
368
|
+
// Also update lastConnected timestamp
|
|
369
|
+
const printer = this.printers.get(id);
|
|
370
|
+
if (printer) {
|
|
371
|
+
this.printers.set(id, { ...printer, lastConnected: Date.now() });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
this.save();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get last used printer ID
|
|
379
|
+
*/
|
|
380
|
+
getLastUsedId(): string | null {
|
|
381
|
+
return this.lastUsedPrinterId;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get global configuration
|
|
386
|
+
*/
|
|
387
|
+
getGlobalConfig(): GlobalConfig {
|
|
388
|
+
return { ...this.globalConfig };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Update global configuration
|
|
393
|
+
*/
|
|
394
|
+
updateGlobalConfig(updates: Partial<GlobalConfig>): void {
|
|
395
|
+
this.globalConfig = { ...this.globalConfig, ...updates };
|
|
396
|
+
this.save();
|
|
397
|
+
this.logger.debug('Global config updated');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Reset global config to defaults
|
|
402
|
+
*/
|
|
403
|
+
resetGlobalConfig(): void {
|
|
404
|
+
this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG };
|
|
405
|
+
this.save();
|
|
406
|
+
this.logger.info('Global config reset to defaults');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Load print config for a printer (with defaults applied)
|
|
411
|
+
*/
|
|
412
|
+
loadPrinterConfig(printerId: string): PrintConfig {
|
|
413
|
+
const printer = this.printers.get(printerId);
|
|
414
|
+
const printConfig = printer?.printConfig || {};
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
encoding: printConfig.encoding ?? this.globalConfig.defaultEncoding,
|
|
418
|
+
chunkSize: printConfig.chunkSize ?? this.globalConfig.defaultChunkSize,
|
|
419
|
+
chunkDelay: printConfig.chunkDelay ?? this.globalConfig.defaultChunkDelay,
|
|
420
|
+
retries: printConfig.retries ?? this.globalConfig.defaultRetries,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Export all configuration as JSON
|
|
426
|
+
*/
|
|
427
|
+
export(): string {
|
|
428
|
+
return JSON.stringify({
|
|
429
|
+
printers: Array.from(this.printers.values()),
|
|
430
|
+
globalConfig: this.globalConfig,
|
|
431
|
+
lastUsedPrinterId: this.lastUsedPrinterId,
|
|
432
|
+
exportedAt: Date.now(),
|
|
433
|
+
}, null, 2);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Import configuration from JSON
|
|
438
|
+
*
|
|
439
|
+
* @param json - JSON string to import
|
|
440
|
+
* @param merge - If true, merge with existing config; if false, replace
|
|
441
|
+
* @returns Number of printers imported
|
|
442
|
+
*/
|
|
443
|
+
import(json: string, merge = false): number {
|
|
444
|
+
try {
|
|
445
|
+
const data = JSON.parse(json) as {
|
|
446
|
+
printers?: SavedPrinter[];
|
|
447
|
+
globalConfig?: GlobalConfig;
|
|
448
|
+
lastUsedPrinterId?: string;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
let imported = 0;
|
|
452
|
+
|
|
453
|
+
if (!merge) {
|
|
454
|
+
this.printers.clear();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (data.printers && Array.isArray(data.printers)) {
|
|
458
|
+
for (const printer of data.printers) {
|
|
459
|
+
if (printer.id) {
|
|
460
|
+
this.printers.set(printer.id, printer);
|
|
461
|
+
imported++;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (data.globalConfig) {
|
|
467
|
+
this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG, ...data.globalConfig };
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (data.lastUsedPrinterId && this.printers.has(data.lastUsedPrinterId)) {
|
|
471
|
+
this.lastUsedPrinterId = data.lastUsedPrinterId;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
this.save();
|
|
475
|
+
this.logger.info(`Imported ${imported} printers`);
|
|
476
|
+
|
|
477
|
+
return imported;
|
|
478
|
+
} catch (error) {
|
|
479
|
+
this.logger.error('Failed to import configuration:', error);
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Clear all configuration
|
|
486
|
+
*/
|
|
487
|
+
clear(): void {
|
|
488
|
+
this.printers.clear();
|
|
489
|
+
this.lastUsedPrinterId = null;
|
|
490
|
+
this.storage.clear();
|
|
491
|
+
this.logger.info('All configuration cleared');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Get statistics
|
|
496
|
+
*/
|
|
497
|
+
getStats(): {
|
|
498
|
+
printerCount: number;
|
|
499
|
+
hasDefault: boolean;
|
|
500
|
+
lastUsed: string | null;
|
|
501
|
+
} {
|
|
502
|
+
let hasDefault = false;
|
|
503
|
+
for (const printer of this.printers.values()) {
|
|
504
|
+
if (printer.isDefault) {
|
|
505
|
+
hasDefault = true;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
printerCount: this.printers.size,
|
|
512
|
+
hasDefault,
|
|
513
|
+
lastUsed: this.lastUsedPrinterId,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Export singleton for convenience
|
|
519
|
+
export const printerConfigManager = new PrinterConfigManager();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Module
|
|
3
|
+
* 配置模块 - 提供打印机配置管理
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { PrinterConfigManager, printerConfigManager } from './PrinterConfigManager';
|
|
7
|
+
export type {
|
|
8
|
+
SavedPrinter,
|
|
9
|
+
PrintConfig,
|
|
10
|
+
GlobalConfig,
|
|
11
|
+
IConfigStorage,
|
|
12
|
+
} from './PrinterConfigManager';
|
|
13
|
+
|
|
14
|
+
export { DEFAULT_CONFIG, mergeConfig } from './PrinterConfig';
|
|
15
|
+
export type { PrinterConfig, AdapterConfig, DriverConfig, LoggingConfig } from './PrinterConfig';
|
|
@@ -60,7 +60,7 @@ export interface PrinterEvents {
|
|
|
60
60
|
* ```
|
|
61
61
|
*/
|
|
62
62
|
export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
63
|
-
private readonly
|
|
63
|
+
private readonly printerLogger = Logger.scope('BluetoothPrinter');
|
|
64
64
|
|
|
65
65
|
/** Current printer state */
|
|
66
66
|
public state: PrinterState = PrinterState.DISCONNECTED;
|
|
@@ -155,7 +155,7 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
this.emit('state-change', this.state);
|
|
158
|
-
this.
|
|
158
|
+
this.printerLogger.debug('State updated:', this.state);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/**
|
|
@@ -171,13 +171,13 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
171
171
|
* ```
|
|
172
172
|
*/
|
|
173
173
|
async connect(deviceId: string): Promise<this> {
|
|
174
|
-
this.
|
|
174
|
+
this.printerLogger.info('Connecting to device:', deviceId);
|
|
175
175
|
|
|
176
176
|
try {
|
|
177
177
|
await this.connectionManager.connect(deviceId);
|
|
178
178
|
this.updateState();
|
|
179
179
|
this.emit('connected', deviceId);
|
|
180
|
-
this.
|
|
180
|
+
this.printerLogger.info('Connected successfully');
|
|
181
181
|
|
|
182
182
|
return this;
|
|
183
183
|
} catch (error) {
|
|
@@ -208,18 +208,18 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
208
208
|
async disconnect(): Promise<void> {
|
|
209
209
|
const deviceId = this.connectionManager.getDeviceId();
|
|
210
210
|
if (!deviceId) {
|
|
211
|
-
this.
|
|
211
|
+
this.printerLogger.warn('Disconnect called but no device connected');
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
this.
|
|
215
|
+
this.printerLogger.info('Disconnecting from device:', deviceId);
|
|
216
216
|
|
|
217
217
|
try {
|
|
218
218
|
await this.connectionManager.disconnect();
|
|
219
219
|
this.printJobManager.cancel();
|
|
220
220
|
this.updateState();
|
|
221
221
|
this.emit('disconnected', deviceId);
|
|
222
|
-
this.
|
|
222
|
+
this.printerLogger.info('Disconnected successfully');
|
|
223
223
|
} catch (error) {
|
|
224
224
|
const printError = new BluetoothPrintError(
|
|
225
225
|
ErrorCode.DEVICE_DISCONNECTED,
|
|
@@ -346,7 +346,7 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
346
346
|
pause(): void {
|
|
347
347
|
this.printJobManager.pause();
|
|
348
348
|
this.updateState();
|
|
349
|
-
this.
|
|
349
|
+
this.printerLogger.info('Print job paused');
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
/**
|
|
@@ -360,12 +360,12 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
360
360
|
* ```
|
|
361
361
|
*/
|
|
362
362
|
async resume(): Promise<void> {
|
|
363
|
-
this.
|
|
363
|
+
this.printerLogger.info('Resuming print job');
|
|
364
364
|
|
|
365
365
|
try {
|
|
366
366
|
await this.printJobManager.resume();
|
|
367
367
|
this.updateState();
|
|
368
|
-
this.
|
|
368
|
+
this.printerLogger.info('Print job resumed');
|
|
369
369
|
} catch (error) {
|
|
370
370
|
const printError = new BluetoothPrintError(
|
|
371
371
|
ErrorCode.PRINT_JOB_FAILED,
|
|
@@ -390,7 +390,7 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
390
390
|
this.printJobManager.cancel();
|
|
391
391
|
this.commandBuilder.clear();
|
|
392
392
|
this.updateState();
|
|
393
|
-
this.
|
|
393
|
+
this.printerLogger.info('Print job cancelled');
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
/**
|
|
@@ -439,7 +439,7 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
const buffer = this.commandBuilder.getBuffer();
|
|
442
|
-
this.
|
|
442
|
+
this.printerLogger.info(`Starting print job: ${buffer.length} bytes`);
|
|
443
443
|
|
|
444
444
|
// Clear the command buffer after getting the buffer for printing
|
|
445
445
|
this.commandBuilder.clear();
|
|
@@ -462,14 +462,14 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
|
|
|
462
462
|
|
|
463
463
|
if (isPaused) {
|
|
464
464
|
// Print job was paused
|
|
465
|
-
this.
|
|
465
|
+
this.printerLogger.info('Print job paused');
|
|
466
466
|
} else {
|
|
467
467
|
// Print job completed successfully
|
|
468
468
|
this.emit('print-complete');
|
|
469
|
-
this.
|
|
469
|
+
this.printerLogger.info('Print job completed successfully');
|
|
470
470
|
}
|
|
471
471
|
} catch (error) {
|
|
472
|
-
this.
|
|
472
|
+
this.printerLogger.error('Print job failed with error:', error);
|
|
473
473
|
const printError =
|
|
474
474
|
error instanceof BluetoothPrintError
|
|
475
475
|
? error
|