taro-bluetooth-print 2.10.2 → 2.12.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 +19 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/BaseAdapter.d.ts +29 -2
- package/dist/types/adapters/ChunkWriteStrategy.d.ts +33 -0
- package/dist/types/adapters/ReactNativeAdapter.d.ts +1 -1
- package/dist/types/cache/OfflineCache.d.ts +41 -9
- package/dist/types/constants/strings.d.ts +136 -0
- package/dist/types/core/EventEmitter.d.ts +8 -1
- package/dist/types/core/event/EventBus.d.ts +1 -1
- package/dist/types/encoding/EncodingService.d.ts +17 -0
- package/dist/types/errors/baseError.d.ts +4 -1
- package/dist/types/preview/PreviewRenderer.d.ts +27 -0
- package/dist/types/queue/PrintQueue.d.ts +33 -1
- package/dist/types/services/BatchPrintManager.d.ts +22 -27
- package/dist/types/services/PrintHistory.d.ts +16 -1
- package/dist/types/services/PrintJobManager.d.ts +10 -0
- package/dist/types/services/PrintScheduler.d.ts +3 -0
- package/dist/types/template/TemplateEngine.d.ts +2 -0
- package/dist/types/template/engines/TemplateRenderer.d.ts +2 -0
- package/dist/types/utils/bitmap.d.ts +17 -0
- package/dist/types/utils/errorHandler.d.ts +78 -0
- package/dist/types/utils/image.d.ts +62 -10
- package/package.json +2 -1
- package/src/adapters/AdapterFactory.ts +3 -29
- package/src/adapters/AlipayAdapter.ts +2 -3
- package/src/adapters/BaiduAdapter.ts +2 -3
- package/src/adapters/BaseAdapter.ts +102 -58
- package/src/adapters/ByteDanceAdapter.ts +2 -3
- package/src/adapters/ChunkWriteStrategy.ts +236 -80
- package/src/adapters/QQAdapter.ts +2 -3
- package/src/adapters/ReactNativeAdapter.ts +13 -58
- package/src/adapters/TaroAdapter.ts +2 -3
- package/src/adapters/WebBluetoothAdapter.ts +1 -3
- package/src/barcode/BarcodeGenerator.ts +1 -2
- package/src/cache/OfflineCache.ts +180 -27
- package/src/config/PrinterConfigManager.ts +2 -1
- package/src/constants/strings.ts +186 -0
- package/src/core/EventEmitter.ts +33 -32
- package/src/core/di/Container.ts +8 -19
- package/src/core/event/EventBus.ts +9 -13
- package/src/core/plugin/PluginManager.ts +0 -4
- package/src/device/DeviceManager.ts +7 -2
- package/src/device/DiscoveryService.ts +0 -4
- package/src/device/MultiPrinterManager.ts +8 -7
- package/src/drivers/CpclDriver.ts +3 -7
- package/src/drivers/XprinterDriver.ts +1 -2
- package/src/drivers/ZplDriver.ts +3 -7
- package/src/encoding/EncodingService.ts +106 -202
- package/src/errors/baseError.ts +7 -0
- package/src/factory/PrinterFactory.ts +3 -1
- package/src/preview/PreviewRenderer.ts +324 -173
- package/src/providers/ServiceProvider.ts +2 -6
- package/src/queue/PrintQueue.ts +135 -32
- package/src/services/BatchPrintManager.ts +153 -130
- package/src/services/CloudPrintManager.ts +2 -1
- package/src/services/ConnectionManager.ts +5 -1
- package/src/services/PrintHistory.ts +60 -10
- package/src/services/PrintJobManager.ts +58 -19
- package/src/services/PrintScheduler.ts +154 -26
- package/src/services/PrintStatistics.ts +3 -4
- package/src/template/TemplateEngine.ts +4 -1
- package/src/template/engines/TemplateRenderer.ts +5 -7
- package/src/template/parsers/TemplateParser.ts +7 -2
- package/src/utils/bitmap.ts +25 -0
- package/src/utils/errorHandler.ts +196 -0
- package/src/utils/image.ts +375 -270
- package/src/utils/outputLimiter.ts +7 -4
|
@@ -54,7 +54,7 @@ export interface AdaptiveWriteConfig {
|
|
|
54
54
|
* Default adaptive write configuration
|
|
55
55
|
*/
|
|
56
56
|
export const DEFAULT_ADAPTIVE_CONFIG: AdaptiveWriteConfig = {
|
|
57
|
-
defaultChunkSize:
|
|
57
|
+
defaultChunkSize: 50, // 增加初始 chunk size,减少 BLE 通信开销
|
|
58
58
|
defaultDelay: 20,
|
|
59
59
|
defaultRetries: 3,
|
|
60
60
|
minChunkSize: 10,
|
|
@@ -62,7 +62,7 @@ export const DEFAULT_ADAPTIVE_CONFIG: AdaptiveWriteConfig = {
|
|
|
62
62
|
maxDelay: 200,
|
|
63
63
|
successThreshold: 3,
|
|
64
64
|
failureThreshold: 2,
|
|
65
|
-
connectionCheckInterval:
|
|
65
|
+
connectionCheckInterval: 10, // 增加连接检查间隔,减少不必要的检查
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -116,12 +116,31 @@ export abstract class ChunkWriteStrategy<TOptions = void> {
|
|
|
116
116
|
// default: no connection check
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/** Step size for increasing chunk size on success (bytes) */
|
|
120
|
+
private static readonly CHUNK_SIZE_STEP = 5;
|
|
121
|
+
/** Delay backoff multiplier on failure */
|
|
122
|
+
private static readonly DELAY_BACKOFF_FACTOR = 1.5;
|
|
123
|
+
/** Delay recovery divisor on success */
|
|
124
|
+
private static readonly DELAY_RECOVERY_FACTOR = 1.2;
|
|
125
|
+
/** Timeout base (ms) for computeTimeoutMs */
|
|
126
|
+
private static readonly TIMEOUT_BASE_MS = 1000;
|
|
127
|
+
/** Timeout per-byte factor (ms) for computeTimeoutMs */
|
|
128
|
+
private static readonly TIMEOUT_PER_BYTE_MS = 5;
|
|
129
|
+
/** Maximum timeout (ms) for computeTimeoutMs */
|
|
130
|
+
private static readonly TIMEOUT_MAX_MS = 10000;
|
|
131
|
+
|
|
119
132
|
/**
|
|
120
133
|
* Compute chunk write timeout based on chunk size.
|
|
121
134
|
* Override to customize timeout formula per platform.
|
|
122
135
|
*/
|
|
123
136
|
protected computeTimeoutMs(chunkLength: number): number {
|
|
124
|
-
return Math.max(
|
|
137
|
+
return Math.max(
|
|
138
|
+
ChunkWriteStrategy.TIMEOUT_BASE_MS,
|
|
139
|
+
Math.min(
|
|
140
|
+
ChunkWriteStrategy.TIMEOUT_MAX_MS,
|
|
141
|
+
ChunkWriteStrategy.TIMEOUT_BASE_MS + chunkLength * ChunkWriteStrategy.TIMEOUT_PER_BYTE_MS
|
|
142
|
+
)
|
|
143
|
+
);
|
|
125
144
|
}
|
|
126
145
|
|
|
127
146
|
/**
|
|
@@ -150,99 +169,236 @@ export abstract class ChunkWriteStrategy<TOptions = void> {
|
|
|
150
169
|
): Promise<void> {
|
|
151
170
|
const data = new Uint8Array(buffer);
|
|
152
171
|
|
|
153
|
-
const chunkSize = Math.max(
|
|
154
|
-
1,
|
|
155
|
-
Math.min(256, adapterOptions.chunkSize ?? this.config.defaultChunkSize)
|
|
156
|
-
);
|
|
157
|
-
const delay = Math.max(10, Math.min(100, adapterOptions.delay ?? this.config.defaultDelay));
|
|
158
|
-
const retries = Math.max(1, Math.min(10, adapterOptions.retries ?? this.config.defaultRetries));
|
|
159
|
-
|
|
160
|
-
const maxChunkSize = this.getMaxChunkSize(context.deviceId);
|
|
161
|
-
|
|
162
|
-
let currentChunkSize = chunkSize;
|
|
163
|
-
let baseDelay = delay;
|
|
164
|
-
let totalChunks = Math.ceil(data.length / currentChunkSize);
|
|
165
|
-
|
|
166
|
-
this.logger.debug(`Writing ${data.length} bytes in ${totalChunks} chunks`);
|
|
167
|
-
|
|
168
172
|
if (data.length === 0) {
|
|
169
173
|
this.logger.warn('No data to write');
|
|
170
174
|
return;
|
|
171
175
|
}
|
|
172
176
|
|
|
177
|
+
// Initialize transmission parameters
|
|
178
|
+
const params = this.initializeTransmissionParams(data, adapterOptions);
|
|
179
|
+
const maxChunkSize = this.getMaxChunkSize(context.deviceId);
|
|
180
|
+
|
|
173
181
|
// Adaptive transmission state
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
const adaptiveState = {
|
|
183
|
+
successCount: 0,
|
|
184
|
+
consecutiveFailures: 0,
|
|
185
|
+
currentChunkSize: params.chunkSize,
|
|
186
|
+
baseDelay: params.delay,
|
|
187
|
+
};
|
|
178
188
|
|
|
179
|
-
|
|
189
|
+
this.logger.debug(
|
|
190
|
+
`Writing ${data.length} bytes with initial chunkSize=${adaptiveState.currentChunkSize}, delay=${adaptiveState.baseDelay}ms`
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Main transmission loop
|
|
194
|
+
for (let i = 0; i < data.length; i += adaptiveState.currentChunkSize) {
|
|
180
195
|
// Periodic connection state check
|
|
181
|
-
|
|
182
|
-
|
|
196
|
+
await this.maybeCheckConnection(i, adaptiveState.currentChunkSize, context.deviceId);
|
|
197
|
+
|
|
198
|
+
// Write current chunk with retries
|
|
199
|
+
const chunkNum = Math.floor(i / adaptiveState.currentChunkSize) + 1;
|
|
200
|
+
const writeSuccess = await this.writeChunkWithRetries(
|
|
201
|
+
data,
|
|
202
|
+
i,
|
|
203
|
+
adaptiveState.currentChunkSize,
|
|
204
|
+
context,
|
|
205
|
+
chunkNum,
|
|
206
|
+
params.retries,
|
|
207
|
+
adaptiveState.baseDelay,
|
|
208
|
+
platformOptions
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!writeSuccess) {
|
|
212
|
+
// All retries exhausted - error already thrown
|
|
213
|
+
return;
|
|
183
214
|
}
|
|
184
215
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
// Adaptive adjustment
|
|
217
|
+
this.adjustTransmissionParams(
|
|
218
|
+
adaptiveState,
|
|
219
|
+
writeSuccess,
|
|
220
|
+
maxChunkSize,
|
|
221
|
+
params.minChunkSize,
|
|
222
|
+
params.maxDelay,
|
|
223
|
+
params.successThreshold,
|
|
224
|
+
params.failureThreshold,
|
|
225
|
+
chunkNum,
|
|
226
|
+
i,
|
|
227
|
+
data.length
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Inter-chunk delay
|
|
231
|
+
await this.maybeDelay(
|
|
232
|
+
i,
|
|
233
|
+
adaptiveState.currentChunkSize,
|
|
234
|
+
data.length,
|
|
235
|
+
adaptiveState.baseDelay
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.logger.info(`Successfully wrote ${data.length} bytes`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Initialize transmission parameters from adapter options
|
|
244
|
+
*/
|
|
245
|
+
private initializeTransmissionParams(
|
|
246
|
+
_data: Uint8Array,
|
|
247
|
+
options: IAdapterOptions
|
|
248
|
+
): {
|
|
249
|
+
chunkSize: number;
|
|
250
|
+
delay: number;
|
|
251
|
+
retries: number;
|
|
252
|
+
minChunkSize: number;
|
|
253
|
+
maxDelay: number;
|
|
254
|
+
successThreshold: number;
|
|
255
|
+
failureThreshold: number;
|
|
256
|
+
} {
|
|
257
|
+
const chunkSize = Math.max(1, Math.min(256, options.chunkSize ?? this.config.defaultChunkSize));
|
|
258
|
+
const delay = Math.max(10, Math.min(100, options.delay ?? this.config.defaultDelay));
|
|
259
|
+
const retries = Math.max(1, Math.min(10, options.retries ?? this.config.defaultRetries));
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
chunkSize,
|
|
263
|
+
delay,
|
|
264
|
+
retries,
|
|
265
|
+
minChunkSize: this.config.minChunkSize,
|
|
266
|
+
maxDelay: this.config.maxDelay,
|
|
267
|
+
successThreshold: this.config.successThreshold,
|
|
268
|
+
failureThreshold: this.config.failureThreshold,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Check connection periodically based on interval
|
|
274
|
+
*/
|
|
275
|
+
private async maybeCheckConnection(
|
|
276
|
+
currentIndex: number,
|
|
277
|
+
chunkSize: number,
|
|
278
|
+
deviceId: string
|
|
279
|
+
): Promise<void> {
|
|
280
|
+
if (
|
|
281
|
+
currentIndex > 0 &&
|
|
282
|
+
Math.floor(currentIndex / chunkSize) % this.config.connectionCheckInterval === 0
|
|
283
|
+
) {
|
|
284
|
+
await this.checkConnection(deviceId);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Write a single chunk with retry logic
|
|
290
|
+
* @returns true if successful, false if all retries exhausted (error thrown)
|
|
291
|
+
*/
|
|
292
|
+
private async writeChunkWithRetries(
|
|
293
|
+
data: Uint8Array,
|
|
294
|
+
startIndex: number,
|
|
295
|
+
chunkSize: number,
|
|
296
|
+
context: ChunkWriteContext,
|
|
297
|
+
chunkNum: number,
|
|
298
|
+
maxRetries: number,
|
|
299
|
+
baseDelay: number,
|
|
300
|
+
platformOptions?: TOptions
|
|
301
|
+
): Promise<boolean> {
|
|
302
|
+
const chunk = data.slice(startIndex, startIndex + chunkSize);
|
|
303
|
+
const totalChunks = Math.ceil(data.length / chunkSize);
|
|
304
|
+
|
|
305
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
306
|
+
const writeResult = await this.writeSingleChunk(chunk, context, platformOptions);
|
|
307
|
+
|
|
308
|
+
if (writeResult.success) {
|
|
309
|
+
this.logger.debug(`Chunk ${chunkNum}/${totalChunks} written successfully`);
|
|
310
|
+
return true;
|
|
213
311
|
}
|
|
214
312
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
313
|
+
if (attempt >= maxRetries) {
|
|
314
|
+
this.logger.error(`Chunk ${chunkNum} failed after ${maxRetries} retries`);
|
|
315
|
+
throw new BluetoothPrintError(
|
|
316
|
+
ErrorCode.WRITE_FAILED,
|
|
317
|
+
`Failed to write chunk ${chunkNum}/${totalChunks}`,
|
|
318
|
+
writeResult.error ?? new Error('Unknown write error')
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this.logger.warn(`Chunk ${chunkNum} write failed, retry ${attempt + 1}/${maxRetries}`);
|
|
323
|
+
|
|
324
|
+
// Exponential backoff
|
|
325
|
+
const retryDelay = baseDelay * Math.pow(2, attempt);
|
|
326
|
+
await new Promise(r => setTimeout(r, Math.min(retryDelay, this.config.maxDelay)));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return false; // Unreachable, but satisfies TypeScript
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Adjust chunk size and delay based on success/failure
|
|
334
|
+
*/
|
|
335
|
+
private adjustTransmissionParams(
|
|
336
|
+
state: {
|
|
337
|
+
successCount: number;
|
|
338
|
+
consecutiveFailures: number;
|
|
339
|
+
currentChunkSize: number;
|
|
340
|
+
baseDelay: number;
|
|
341
|
+
},
|
|
342
|
+
writeSuccess: boolean,
|
|
343
|
+
maxChunkSize: number,
|
|
344
|
+
minChunkSize: number,
|
|
345
|
+
maxDelay: number,
|
|
346
|
+
successThreshold: number,
|
|
347
|
+
failureThreshold: number,
|
|
348
|
+
_chunkNum: number,
|
|
349
|
+
_currentIndex: number,
|
|
350
|
+
_totalLength: number
|
|
351
|
+
): void {
|
|
352
|
+
if (writeSuccess) {
|
|
353
|
+
state.successCount++;
|
|
354
|
+
state.consecutiveFailures = 0;
|
|
355
|
+
|
|
356
|
+
// Increase chunk size on sustained success
|
|
357
|
+
if (state.successCount % successThreshold === 0 && state.currentChunkSize < maxChunkSize) {
|
|
358
|
+
state.currentChunkSize = Math.min(
|
|
359
|
+
maxChunkSize,
|
|
360
|
+
state.currentChunkSize + ChunkWriteStrategy.CHUNK_SIZE_STEP
|
|
361
|
+
);
|
|
362
|
+
state.baseDelay = Math.max(
|
|
363
|
+
state.baseDelay / ChunkWriteStrategy.DELAY_RECOVERY_FACTOR,
|
|
364
|
+
this.config.defaultDelay
|
|
365
|
+
);
|
|
366
|
+
this.logger.debug(
|
|
367
|
+
`Increased chunk size to ${state.currentChunkSize}, delay to ${state.baseDelay}ms`
|
|
368
|
+
);
|
|
238
369
|
}
|
|
370
|
+
} else {
|
|
371
|
+
state.consecutiveFailures++;
|
|
239
372
|
|
|
240
|
-
//
|
|
241
|
-
if (
|
|
242
|
-
|
|
373
|
+
// Decrease chunk size on sustained failure
|
|
374
|
+
if (state.consecutiveFailures >= failureThreshold && state.currentChunkSize > minChunkSize) {
|
|
375
|
+
state.currentChunkSize = Math.max(
|
|
376
|
+
minChunkSize,
|
|
377
|
+
state.currentChunkSize - ChunkWriteStrategy.CHUNK_SIZE_STEP
|
|
378
|
+
);
|
|
379
|
+
state.baseDelay = Math.min(
|
|
380
|
+
state.baseDelay * ChunkWriteStrategy.DELAY_BACKOFF_FACTOR,
|
|
381
|
+
maxDelay
|
|
382
|
+
);
|
|
383
|
+
this.logger.debug(
|
|
384
|
+
`Decreased chunk size to ${state.currentChunkSize}, delay to ${state.baseDelay}ms`
|
|
385
|
+
);
|
|
386
|
+
state.consecutiveFailures = 0;
|
|
243
387
|
}
|
|
244
388
|
}
|
|
389
|
+
}
|
|
245
390
|
|
|
246
|
-
|
|
391
|
+
/**
|
|
392
|
+
* Apply inter-chunk delay to prevent BLE congestion
|
|
393
|
+
*/
|
|
394
|
+
private async maybeDelay(
|
|
395
|
+
currentIndex: number,
|
|
396
|
+
chunkSize: number,
|
|
397
|
+
totalLength: number,
|
|
398
|
+
delay: number
|
|
399
|
+
): Promise<void> {
|
|
400
|
+
if (currentIndex + chunkSize < totalLength) {
|
|
401
|
+
await new Promise(r => setTimeout(r, delay));
|
|
402
|
+
}
|
|
247
403
|
}
|
|
248
404
|
}
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
interface QQGlobal extends MiniProgramBLEApi {}
|
|
8
|
+
// QQ global type for TypeScript
|
|
9
|
+
type QQGlobal = MiniProgramBLEApi;
|
|
11
10
|
|
|
12
11
|
declare const qq: QQGlobal;
|
|
13
12
|
|
|
@@ -193,13 +193,14 @@ export class ReactNativeAdapter extends BaseAdapter implements IPrinterAdapter {
|
|
|
193
193
|
*
|
|
194
194
|
* @param options - Configuration options
|
|
195
195
|
* @param options.bleManager - BLE Manager instance (e.g., from react-native-ble-plx)
|
|
196
|
-
* @throws {
|
|
196
|
+
* @throws {BluetoothPrintError} If bleManager is not provided or not supported
|
|
197
197
|
*/
|
|
198
198
|
constructor(options: { bleManager: BLEManager }) {
|
|
199
199
|
super();
|
|
200
200
|
|
|
201
201
|
if (!options?.bleManager) {
|
|
202
|
-
throw new
|
|
202
|
+
throw new BluetoothPrintError(
|
|
203
|
+
ErrorCode.INVALID_CONFIGURATION,
|
|
203
204
|
'ReactNativeAdapter requires a bleManager instance (e.g., react-native-ble-plx). ' +
|
|
204
205
|
'Please pass { bleManager: yourBleManager } in the constructor.'
|
|
205
206
|
);
|
|
@@ -251,26 +252,7 @@ export class ReactNativeAdapter extends BaseAdapter implements IPrinterAdapter {
|
|
|
251
252
|
this.updateState(PrinterState.DISCONNECTED);
|
|
252
253
|
this.cleanupDevice(deviceId);
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
if (errorMsg.includes('timeout')) {
|
|
256
|
-
throw new BluetoothPrintError(
|
|
257
|
-
ErrorCode.CONNECTION_TIMEOUT,
|
|
258
|
-
`Connection to device ${deviceId} timed out`,
|
|
259
|
-
normalizeError(error)
|
|
260
|
-
);
|
|
261
|
-
} else if (errorMsg.includes('not found') || errorMsg.includes('not exist')) {
|
|
262
|
-
throw new BluetoothPrintError(
|
|
263
|
-
ErrorCode.DEVICE_NOT_FOUND,
|
|
264
|
-
`Device ${deviceId} not found`,
|
|
265
|
-
normalizeError(error)
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
throw new BluetoothPrintError(
|
|
270
|
-
ErrorCode.CONNECTION_FAILED,
|
|
271
|
-
`Failed to connect to device ${deviceId}`,
|
|
272
|
-
normalizeError(error)
|
|
273
|
-
);
|
|
255
|
+
throw this.classifyConnectionError(error, deviceId);
|
|
274
256
|
}
|
|
275
257
|
}
|
|
276
258
|
|
|
@@ -370,7 +352,6 @@ export class ReactNativeAdapter extends BaseAdapter implements IPrinterAdapter {
|
|
|
370
352
|
try {
|
|
371
353
|
this.bleManager.startDeviceScan(null, { allowDuplicates: false }, (error: unknown) => {
|
|
372
354
|
if (error) {
|
|
373
|
-
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
374
355
|
reject(normalizeError(error));
|
|
375
356
|
}
|
|
376
357
|
});
|
|
@@ -398,47 +379,21 @@ export class ReactNativeAdapter extends BaseAdapter implements IPrinterAdapter {
|
|
|
398
379
|
* @param device - Connected device object
|
|
399
380
|
*/
|
|
400
381
|
private async discoverServices(deviceId: string, _device: RNDevice): Promise<void> {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
try {
|
|
382
|
+
await this.discoverAndCacheServices(deviceId, async () => {
|
|
404
383
|
const servicesResult = await (this.bleManager.discoverAllServicesAndCharacteristicsForDevice(
|
|
405
384
|
deviceId
|
|
406
385
|
) as Promise<{ services: RNService[] }>);
|
|
407
386
|
|
|
408
387
|
const services = servicesResult.services || [];
|
|
409
388
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
characteristicId: writeChar.uuid,
|
|
419
|
-
});
|
|
420
|
-
Logger.scope('ReactNativeAdapter').info('Found writeable characteristic:', {
|
|
421
|
-
service: service.uuid,
|
|
422
|
-
characteristic: writeChar.uuid,
|
|
423
|
-
});
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
throw new BluetoothPrintError(
|
|
429
|
-
ErrorCode.CHARACTERISTIC_NOT_FOUND,
|
|
430
|
-
'No writeable characteristic found. Make sure the device is a supported printer.'
|
|
431
|
-
);
|
|
432
|
-
} catch (error) {
|
|
433
|
-
if (error instanceof BluetoothPrintError) {
|
|
434
|
-
throw error;
|
|
435
|
-
}
|
|
436
|
-
throw new BluetoothPrintError(
|
|
437
|
-
ErrorCode.SERVICE_DISCOVERY_FAILED,
|
|
438
|
-
'Failed to discover device services',
|
|
439
|
-
normalizeError(error)
|
|
440
|
-
);
|
|
441
|
-
}
|
|
389
|
+
return services.map(service => ({
|
|
390
|
+
serviceId: service.uuid,
|
|
391
|
+
characteristics: service.characteristics.map((c: RNCharacteristic) => ({
|
|
392
|
+
characteristicId: c.uuid,
|
|
393
|
+
isWritable: c.isWritableWithResponse || c.isWritableWithoutResponse,
|
|
394
|
+
})),
|
|
395
|
+
}));
|
|
396
|
+
});
|
|
442
397
|
}
|
|
443
398
|
|
|
444
399
|
/**
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
interface TaroGlobal extends MiniProgramBLEApi {}
|
|
8
|
+
// Taro global type for TypeScript
|
|
9
|
+
type TaroGlobal = MiniProgramBLEApi;
|
|
11
10
|
|
|
12
11
|
declare const Taro: TaroGlobal;
|
|
13
12
|
|
|
@@ -413,9 +413,7 @@ export class WebBluetoothAdapter extends BaseAdapter {
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
// Use device.id if available, otherwise fall back to generated ID
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return deviceId;
|
|
416
|
+
return device.id || this.generateFallbackDeviceId(device);
|
|
419
417
|
}
|
|
420
418
|
|
|
421
419
|
/**
|
|
@@ -566,8 +566,7 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
566
566
|
*/
|
|
567
567
|
private validateCode39(content: string, errors: ValidationResult['errors']): void {
|
|
568
568
|
// Code 39 supports: 0-9, A-Z, space, - . $ / + %
|
|
569
|
-
|
|
570
|
-
const validChars = /^[0-9A-Z\s\-.$\/+%]+$/;
|
|
569
|
+
const validChars = /^[0-9A-Z\s\-.$/+%]+$/;
|
|
571
570
|
if (!validChars.test(content.toUpperCase())) {
|
|
572
571
|
errors.push({
|
|
573
572
|
field: 'content',
|