incyclist-devices 3.0.26 → 3.0.28
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/lib/cjs/ble/base/adapter.js +39 -8
- package/lib/cjs/ble/base/peripheral.js +83 -9
- package/lib/cjs/ble/base/sensor.js +1 -0
- package/lib/cjs/ble/fm/adapter.js +15 -4
- package/lib/cjs/ble/fm/sensor.js +10 -4
- package/lib/cjs/utils/task.js +14 -1
- package/lib/esm/ble/base/adapter.js +39 -8
- package/lib/esm/ble/base/peripheral.js +83 -9
- package/lib/esm/ble/base/sensor.js +1 -0
- package/lib/esm/ble/fm/adapter.js +15 -4
- package/lib/esm/ble/fm/sensor.js +10 -4
- package/lib/esm/utils/task.js +14 -1
- package/lib/types/ble/base/adapter.d.ts +4 -0
- package/lib/types/ble/base/peripheral.d.ts +4 -2
- package/lib/types/ble/fm/sensor.d.ts +1 -1
- package/lib/types/ble/types.d.ts +1 -0
- package/lib/types/utils/task.d.ts +5 -0
- package/package.json +1 -1
|
@@ -17,6 +17,8 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
17
17
|
onDeviceDisconnectHandler = this.emit.bind(this);
|
|
18
18
|
onDisconnectDoneHandler = this.onDisconnectDone.bind(this);
|
|
19
19
|
startTask;
|
|
20
|
+
startPromise;
|
|
21
|
+
stopPromise;
|
|
20
22
|
constructor(settings, props) {
|
|
21
23
|
super(settings, props);
|
|
22
24
|
this.deviceData = {};
|
|
@@ -198,8 +200,11 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
198
200
|
return 'connected';
|
|
199
201
|
}
|
|
200
202
|
async start(startProps) {
|
|
201
|
-
|
|
202
|
-
await this.
|
|
203
|
+
while (this.stopPromise !== undefined) {
|
|
204
|
+
await this.stopPromise;
|
|
205
|
+
}
|
|
206
|
+
if (this.isStarting() && this.startPromise !== undefined) {
|
|
207
|
+
return this.startPromise;
|
|
203
208
|
}
|
|
204
209
|
const ble = this.getBle();
|
|
205
210
|
ble.once('disconnect-done', this.onDisconnectDoneHandler);
|
|
@@ -209,11 +214,20 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
209
214
|
errorOnTimeout: false,
|
|
210
215
|
log: this.logEvent.bind(this)
|
|
211
216
|
});
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
217
|
+
this.startPromise = this.runStartTask(ble);
|
|
218
|
+
return this.startPromise;
|
|
219
|
+
}
|
|
220
|
+
async runStartTask(ble) {
|
|
221
|
+
try {
|
|
222
|
+
const res = await this.startTask.run();
|
|
223
|
+
if (!res) {
|
|
224
|
+
ble.removeListener('disconnect-done', this.onDisconnectDoneHandler);
|
|
225
|
+
}
|
|
226
|
+
return res;
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
this.startPromise = undefined;
|
|
215
230
|
}
|
|
216
|
-
return res;
|
|
217
231
|
}
|
|
218
232
|
isStarting() {
|
|
219
233
|
return this.startTask?.isRunning();
|
|
@@ -331,7 +345,10 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
331
345
|
}
|
|
332
346
|
const sensor = this.getSensor();
|
|
333
347
|
let connected = await sensor.startSensor();
|
|
334
|
-
await sensor.subscribe();
|
|
348
|
+
const subscribed = await sensor.subscribe();
|
|
349
|
+
if (connected && !subscribed) {
|
|
350
|
+
connected = false;
|
|
351
|
+
}
|
|
335
352
|
if (connected) {
|
|
336
353
|
sensor.on('data', this.onDeviceDataHandler);
|
|
337
354
|
sensor.on('disconnected', this.onDeviceDisconnectHandler);
|
|
@@ -379,9 +396,23 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
379
396
|
return success;
|
|
380
397
|
}
|
|
381
398
|
async stop() {
|
|
399
|
+
if (this.stopPromise !== undefined) {
|
|
400
|
+
return this.stopPromise;
|
|
401
|
+
}
|
|
402
|
+
this.stopPromise = this.runStop();
|
|
403
|
+
try {
|
|
404
|
+
return await this.stopPromise;
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
this.stopPromise = undefined;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
async runStop() {
|
|
382
411
|
this.logEvent({ message: 'stopping device', device: this.getName(), interface: this.getInterface() });
|
|
383
412
|
if (this.isStarting()) {
|
|
384
|
-
|
|
413
|
+
const task = this.startTask;
|
|
414
|
+
await task.stop();
|
|
415
|
+
await task.getUnderlyingPromise()?.catch(() => { });
|
|
385
416
|
}
|
|
386
417
|
this.started = false;
|
|
387
418
|
this.resetData();
|
|
@@ -15,6 +15,7 @@ class BlePeripheral {
|
|
|
15
15
|
disconnecting = false;
|
|
16
16
|
disconnectedSignalled = false;
|
|
17
17
|
discoveredServiceUUIds;
|
|
18
|
+
serviceDiscoveryIncomplete = false;
|
|
18
19
|
discoverServicesPromise;
|
|
19
20
|
discoverCharacteristicsPromise = {};
|
|
20
21
|
onErrorHandler = this.onPeripheralError.bind(this);
|
|
@@ -158,8 +159,26 @@ class BlePeripheral {
|
|
|
158
159
|
const promise = this.discoverServicesPromise;
|
|
159
160
|
const res = await promise;
|
|
160
161
|
(0, utils_js_1.sleep)(0).then(() => { delete this.discoverServicesPromise; });
|
|
162
|
+
const isComplete = this.checkAnnouncedServices(res);
|
|
163
|
+
this.serviceDiscoveryIncomplete = !isComplete;
|
|
164
|
+
if (!isComplete) {
|
|
165
|
+
const { name, address } = this.getInfo();
|
|
166
|
+
this.logEvent({ message: 'service data incomplete - disconnecting', name, address,
|
|
167
|
+
announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
|
|
168
|
+
}
|
|
161
169
|
return res;
|
|
162
170
|
}
|
|
171
|
+
checkAnnouncedServices(discovered) {
|
|
172
|
+
const announced = this.getAnnouncedServices().map(x => (0, utils_js_2.beautifyUUID)(x));
|
|
173
|
+
const toBeChecked = discovered.map(x => (0, utils_js_2.beautifyUUID)(x));
|
|
174
|
+
const cntAnnounced = announced.length;
|
|
175
|
+
let cntVerified = 0;
|
|
176
|
+
for (const s of announced) {
|
|
177
|
+
if (toBeChecked.includes(s))
|
|
178
|
+
cntVerified++;
|
|
179
|
+
}
|
|
180
|
+
return cntAnnounced === cntVerified;
|
|
181
|
+
}
|
|
163
182
|
async _discoverServices() {
|
|
164
183
|
if (!this.getPeripheral())
|
|
165
184
|
return [];
|
|
@@ -309,6 +328,14 @@ class BlePeripheral {
|
|
|
309
328
|
}
|
|
310
329
|
catch { }
|
|
311
330
|
}
|
|
331
|
+
if (this.serviceDiscoveryIncomplete) {
|
|
332
|
+
this.logEvent({ message: 'peripheral subscribe selected failed', uuids, reason: 'service discovery incomplete',
|
|
333
|
+
announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
|
|
334
|
+
this.discoveredServiceUUIds = undefined;
|
|
335
|
+
this.serviceDiscoveryIncomplete = false;
|
|
336
|
+
await this.disconnect().catch(() => { });
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
312
339
|
try {
|
|
313
340
|
if (Object.keys(this.characteristics).length === 0) {
|
|
314
341
|
await this.discoverAllCharacteristics();
|
|
@@ -349,6 +376,11 @@ class BlePeripheral {
|
|
|
349
376
|
const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
350
377
|
const found = [];
|
|
351
378
|
const uuids = [];
|
|
379
|
+
const freshUuids = new Set(res.characteristics.map(c => (0, utils_js_2.beautifyUUID)(c.uuid)));
|
|
380
|
+
Object.keys(this.characteristics).forEach(existing => {
|
|
381
|
+
if (!freshUuids.has(existing))
|
|
382
|
+
delete this.characteristics[existing];
|
|
383
|
+
});
|
|
352
384
|
res.characteristics.forEach(c => {
|
|
353
385
|
this.characteristics[(0, utils_js_2.beautifyUUID)(c.uuid)] = c;
|
|
354
386
|
found.push(c.uuid);
|
|
@@ -367,6 +399,12 @@ class BlePeripheral {
|
|
|
367
399
|
const target = characteristics.map(c => (0, utils_js_2.fullUUID)(c));
|
|
368
400
|
const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], target);
|
|
369
401
|
const found = [];
|
|
402
|
+
const freshUuids = new Set(res.characteristics.map(c => (0, utils_js_2.beautifyUUID)(c.uuid)));
|
|
403
|
+
characteristics.forEach(requested => {
|
|
404
|
+
const requestedUuid = (0, utils_js_2.beautifyUUID)(requested);
|
|
405
|
+
if (!freshUuids.has(requestedUuid))
|
|
406
|
+
delete this.characteristics[requestedUuid];
|
|
407
|
+
});
|
|
370
408
|
res.characteristics.forEach(c => {
|
|
371
409
|
this.characteristics[(0, utils_js_2.beautifyUUID)(c.uuid)] = c;
|
|
372
410
|
found.push(c.uuid);
|
|
@@ -436,25 +474,61 @@ class BlePeripheral {
|
|
|
436
474
|
return Promise.reject(new Error('characteristic not found: ' + characteristicUUID));
|
|
437
475
|
}
|
|
438
476
|
}
|
|
477
|
+
const signal = options?.signal;
|
|
439
478
|
return new Promise((resolve, reject) => {
|
|
479
|
+
let settled = false;
|
|
480
|
+
let onData;
|
|
481
|
+
const cleanup = () => {
|
|
482
|
+
if (onData) {
|
|
483
|
+
c.off('data', onData);
|
|
484
|
+
onData = undefined;
|
|
485
|
+
}
|
|
486
|
+
signal?.removeEventListener('abort', onAbort);
|
|
487
|
+
};
|
|
488
|
+
const settleResolve = (result) => {
|
|
489
|
+
if (settled)
|
|
490
|
+
return;
|
|
491
|
+
settled = true;
|
|
492
|
+
cleanup();
|
|
493
|
+
resolve(result);
|
|
494
|
+
};
|
|
495
|
+
const settleReject = (err) => {
|
|
496
|
+
if (settled)
|
|
497
|
+
return;
|
|
498
|
+
settled = true;
|
|
499
|
+
cleanup();
|
|
500
|
+
reject(err);
|
|
501
|
+
};
|
|
502
|
+
const onAbort = () => {
|
|
503
|
+
settleReject(new Error('aborted'));
|
|
504
|
+
};
|
|
505
|
+
if (signal) {
|
|
506
|
+
if (signal.aborted) {
|
|
507
|
+
settleReject(new Error('aborted'));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
511
|
+
}
|
|
440
512
|
const write = () => {
|
|
441
|
-
if (this.disconnecting || !this.connected)
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
513
|
+
if (this.disconnecting || !this.connected) {
|
|
514
|
+
settleResolve(Buffer.from([]));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
onData = (responseData) => {
|
|
518
|
+
settleResolve(responseData);
|
|
519
|
+
};
|
|
520
|
+
c.on('data', onData);
|
|
447
521
|
this.logEvent({ message: 'write request', characteristic: uuid, data: data.toString('hex'), withoutResponse: options?.withoutResponse === true });
|
|
448
522
|
c.write(data, options?.withoutResponse === true, (err) => {
|
|
449
523
|
if (err)
|
|
450
|
-
|
|
524
|
+
settleReject(err);
|
|
451
525
|
});
|
|
452
526
|
if (options?.withoutResponse) {
|
|
453
|
-
|
|
527
|
+
settleResolve(Buffer.from([]));
|
|
454
528
|
}
|
|
455
529
|
};
|
|
456
530
|
if (!options?.withoutResponse) {
|
|
457
|
-
this.subscribe(characteristicUUID, null).then(
|
|
531
|
+
this.subscribe(characteristicUUID, null).then(() => {
|
|
458
532
|
write();
|
|
459
533
|
});
|
|
460
534
|
}
|
|
@@ -79,6 +79,7 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
79
79
|
}
|
|
80
80
|
this.connectPromise = this.connectPromise ?? this.peripheral.connect();
|
|
81
81
|
const connected = await this.connectPromise;
|
|
82
|
+
delete this.connectPromise;
|
|
82
83
|
if (!connected)
|
|
83
84
|
return false;
|
|
84
85
|
if (!reconnect)
|
|
@@ -225,24 +225,33 @@ class BleFmAdapter extends adapter_js_1.default {
|
|
|
225
225
|
let hasControl = false;
|
|
226
226
|
let tryCnt = 0;
|
|
227
227
|
const sensor = this.getSensor();
|
|
228
|
+
let cancelled = false;
|
|
229
|
+
const controlAbort = new AbortController();
|
|
230
|
+
const abandon = () => {
|
|
231
|
+
if (cancelled)
|
|
232
|
+
return;
|
|
233
|
+
cancelled = true;
|
|
234
|
+
controlAbort.abort();
|
|
235
|
+
};
|
|
228
236
|
const controlPromise = new Promise((resolve) => {
|
|
229
237
|
this.startTask.notifyOnStop(() => {
|
|
238
|
+
abandon();
|
|
230
239
|
resolve(false);
|
|
231
240
|
});
|
|
232
241
|
const waitUntilControl = async () => {
|
|
233
242
|
if (this.supportsVirtualShifting()) {
|
|
234
243
|
await this.initVirtualShifting();
|
|
235
244
|
}
|
|
236
|
-
while (!hasControl && this.isStarting()) {
|
|
245
|
+
while (!hasControl && !cancelled && this.isStarting()) {
|
|
237
246
|
if (tryCnt++ === 0) {
|
|
238
247
|
this.logEvent({ message: 'requesting control', device: this.getName(), interface: this.getInterface() });
|
|
239
248
|
}
|
|
240
|
-
hasControl = await sensor.requestControl();
|
|
249
|
+
hasControl = await sensor.requestControl(controlAbort.signal);
|
|
241
250
|
if (hasControl) {
|
|
242
251
|
this.logEvent({ message: 'control granted', device: this.getName(), interface: this.getInterface() });
|
|
243
252
|
resolve(this.isStarting());
|
|
244
253
|
}
|
|
245
|
-
else {
|
|
254
|
+
else if (!cancelled) {
|
|
246
255
|
await (0, utils_js_1.sleep)(this.requestControlRetryDelay);
|
|
247
256
|
}
|
|
248
257
|
}
|
|
@@ -253,10 +262,12 @@ class BleFmAdapter extends adapter_js_1.default {
|
|
|
253
262
|
timeout: this.requestControlTimeout,
|
|
254
263
|
name: 'establishControl',
|
|
255
264
|
errorOnTimeout: false,
|
|
256
|
-
log: this.logEvent.bind(this)
|
|
265
|
+
log: this.logEvent.bind(this),
|
|
266
|
+
onCancel: abandon
|
|
257
267
|
});
|
|
258
268
|
const granted = await waitTask.run();
|
|
259
269
|
if (!granted && this.isStarting()) {
|
|
270
|
+
abandon();
|
|
260
271
|
this.logEvent({ message: 'could not establish control', device: this.getName(), interface: this.getInterface() });
|
|
261
272
|
throw new Error('could not establish control');
|
|
262
273
|
}
|
package/lib/cjs/ble/fm/sensor.js
CHANGED
|
@@ -105,7 +105,7 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
|
|
|
105
105
|
this.hasControl = false;
|
|
106
106
|
this.ftmsServiceDataAttempts = 0;
|
|
107
107
|
}
|
|
108
|
-
async requestControl() {
|
|
108
|
+
async requestControl(signal) {
|
|
109
109
|
if (this.hasControl) {
|
|
110
110
|
return true;
|
|
111
111
|
}
|
|
@@ -115,7 +115,7 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
|
|
|
115
115
|
this.isCheckingControl = true;
|
|
116
116
|
const data = Buffer.alloc(1);
|
|
117
117
|
data.writeUInt8(0, 0);
|
|
118
|
-
const res = await this.writeFtmsMessage(0, data, { timeout: 5000 });
|
|
118
|
+
const res = await this.writeFtmsMessage(0, data, { timeout: 5000, signal });
|
|
119
119
|
if (res === 1) {
|
|
120
120
|
this.hasControl = true;
|
|
121
121
|
}
|
|
@@ -622,9 +622,15 @@ class BleFitnessMachineDevice extends sensor_js_1.TBleSensor {
|
|
|
622
622
|
let res;
|
|
623
623
|
let tsStart = Date.now();
|
|
624
624
|
if (props?.timeout) {
|
|
625
|
-
|
|
625
|
+
const internalAbort = new AbortController();
|
|
626
|
+
const combined = new AbortController();
|
|
627
|
+
props.signal?.addEventListener('abort', () => combined.abort(), { once: true });
|
|
628
|
+
internalAbort.signal.addEventListener('abort', () => combined.abort(), { once: true });
|
|
629
|
+
const signal = combined.signal;
|
|
630
|
+
res = await new task_js_1.InteruptableTask(this.write(consts_js_1.FTMS_CP, data, { ...props, signal }), {
|
|
626
631
|
timeout: props.timeout ?? 800,
|
|
627
|
-
errorOnTimeout: true
|
|
632
|
+
errorOnTimeout: true,
|
|
633
|
+
onCancel: () => internalAbort.abort()
|
|
628
634
|
}).run();
|
|
629
635
|
}
|
|
630
636
|
else {
|
package/lib/cjs/utils/task.js
CHANGED
|
@@ -10,6 +10,7 @@ class InteruptableTask {
|
|
|
10
10
|
internalEvents = new node_events_1.EventEmitter();
|
|
11
11
|
promise;
|
|
12
12
|
onStopNotifiers = [];
|
|
13
|
+
cancelNotified = false;
|
|
13
14
|
constructor(promise, props) {
|
|
14
15
|
this.state = (props?.state ?? {});
|
|
15
16
|
this.props = props;
|
|
@@ -20,6 +21,9 @@ class InteruptableTask {
|
|
|
20
21
|
getPromise() {
|
|
21
22
|
return this.internalState.promise;
|
|
22
23
|
}
|
|
24
|
+
getUnderlyingPromise() {
|
|
25
|
+
return this.promise;
|
|
26
|
+
}
|
|
23
27
|
getState() {
|
|
24
28
|
return this.state;
|
|
25
29
|
}
|
|
@@ -40,6 +44,7 @@ class InteruptableTask {
|
|
|
40
44
|
const { timeout } = this.props;
|
|
41
45
|
if (timeout) {
|
|
42
46
|
this.internalState.tsTimeout = this.internalState.tsStart + timeout;
|
|
47
|
+
this.internalState.timeoutDuration = timeout;
|
|
43
48
|
this.internalState.onTimeout = this.onTimeout.bind(this);
|
|
44
49
|
this.internalState.timeout = setTimeout(() => { this.internalEvents.emit('timeout'); }, timeout);
|
|
45
50
|
this.internalEvents.on('timeout', this.internalState.onTimeout);
|
|
@@ -53,6 +58,7 @@ class InteruptableTask {
|
|
|
53
58
|
this.sendStopNotification();
|
|
54
59
|
if (this.getState().result === 'completed' || this.getState().result === 'error')
|
|
55
60
|
return;
|
|
61
|
+
this.notifyCancel();
|
|
56
62
|
this.getState().result = 'stopped';
|
|
57
63
|
if (this.props.onDone)
|
|
58
64
|
resolve(this.props.onDone(this.getState()));
|
|
@@ -97,8 +103,9 @@ class InteruptableTask {
|
|
|
97
103
|
if (!this.internalState.timeout)
|
|
98
104
|
return;
|
|
99
105
|
const message = this.props.name ? `${this.props.name} timeout` : 'timeout';
|
|
100
|
-
this.logEvent({ message, active: this.isRunning() });
|
|
106
|
+
this.logEvent({ message, active: this.isRunning(), duration: this.internalState?.timeoutDuration });
|
|
101
107
|
this.clearTimeout();
|
|
108
|
+
this.notifyCancel();
|
|
102
109
|
this.getState().result = 'timeout';
|
|
103
110
|
const resolve = this.internalState.onDone;
|
|
104
111
|
const reject = this.internalState.onError;
|
|
@@ -111,6 +118,12 @@ class InteruptableTask {
|
|
|
111
118
|
else
|
|
112
119
|
resolve(null);
|
|
113
120
|
}
|
|
121
|
+
notifyCancel() {
|
|
122
|
+
if (this.cancelNotified)
|
|
123
|
+
return;
|
|
124
|
+
this.cancelNotified = true;
|
|
125
|
+
this.props.onCancel?.();
|
|
126
|
+
}
|
|
114
127
|
sendStopNotification() {
|
|
115
128
|
this.onStopNotifiers.forEach((cb) => {
|
|
116
129
|
if (typeof cb === 'function')
|
|
@@ -12,6 +12,8 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
12
12
|
onDeviceDisconnectHandler = this.emit.bind(this);
|
|
13
13
|
onDisconnectDoneHandler = this.onDisconnectDone.bind(this);
|
|
14
14
|
startTask;
|
|
15
|
+
startPromise;
|
|
16
|
+
stopPromise;
|
|
15
17
|
constructor(settings, props) {
|
|
16
18
|
super(settings, props);
|
|
17
19
|
this.deviceData = {};
|
|
@@ -193,8 +195,11 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
193
195
|
return 'connected';
|
|
194
196
|
}
|
|
195
197
|
async start(startProps) {
|
|
196
|
-
|
|
197
|
-
await this.
|
|
198
|
+
while (this.stopPromise !== undefined) {
|
|
199
|
+
await this.stopPromise;
|
|
200
|
+
}
|
|
201
|
+
if (this.isStarting() && this.startPromise !== undefined) {
|
|
202
|
+
return this.startPromise;
|
|
198
203
|
}
|
|
199
204
|
const ble = this.getBle();
|
|
200
205
|
ble.once('disconnect-done', this.onDisconnectDoneHandler);
|
|
@@ -204,11 +209,20 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
204
209
|
errorOnTimeout: false,
|
|
205
210
|
log: this.logEvent.bind(this)
|
|
206
211
|
});
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
this.startPromise = this.runStartTask(ble);
|
|
213
|
+
return this.startPromise;
|
|
214
|
+
}
|
|
215
|
+
async runStartTask(ble) {
|
|
216
|
+
try {
|
|
217
|
+
const res = await this.startTask.run();
|
|
218
|
+
if (!res) {
|
|
219
|
+
ble.removeListener('disconnect-done', this.onDisconnectDoneHandler);
|
|
220
|
+
}
|
|
221
|
+
return res;
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
this.startPromise = undefined;
|
|
210
225
|
}
|
|
211
|
-
return res;
|
|
212
226
|
}
|
|
213
227
|
isStarting() {
|
|
214
228
|
return this.startTask?.isRunning();
|
|
@@ -326,7 +340,10 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
326
340
|
}
|
|
327
341
|
const sensor = this.getSensor();
|
|
328
342
|
let connected = await sensor.startSensor();
|
|
329
|
-
await sensor.subscribe();
|
|
343
|
+
const subscribed = await sensor.subscribe();
|
|
344
|
+
if (connected && !subscribed) {
|
|
345
|
+
connected = false;
|
|
346
|
+
}
|
|
330
347
|
if (connected) {
|
|
331
348
|
sensor.on('data', this.onDeviceDataHandler);
|
|
332
349
|
sensor.on('disconnected', this.onDeviceDisconnectHandler);
|
|
@@ -374,9 +391,23 @@ export default class BleAdapter extends IncyclistDevice {
|
|
|
374
391
|
return success;
|
|
375
392
|
}
|
|
376
393
|
async stop() {
|
|
394
|
+
if (this.stopPromise !== undefined) {
|
|
395
|
+
return this.stopPromise;
|
|
396
|
+
}
|
|
397
|
+
this.stopPromise = this.runStop();
|
|
398
|
+
try {
|
|
399
|
+
return await this.stopPromise;
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
this.stopPromise = undefined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async runStop() {
|
|
377
406
|
this.logEvent({ message: 'stopping device', device: this.getName(), interface: this.getInterface() });
|
|
378
407
|
if (this.isStarting()) {
|
|
379
|
-
|
|
408
|
+
const task = this.startTask;
|
|
409
|
+
await task.stop();
|
|
410
|
+
await task.getUnderlyingPromise()?.catch(() => { });
|
|
380
411
|
}
|
|
381
412
|
this.started = false;
|
|
382
413
|
this.resetData();
|
|
@@ -12,6 +12,7 @@ export class BlePeripheral {
|
|
|
12
12
|
disconnecting = false;
|
|
13
13
|
disconnectedSignalled = false;
|
|
14
14
|
discoveredServiceUUIds;
|
|
15
|
+
serviceDiscoveryIncomplete = false;
|
|
15
16
|
discoverServicesPromise;
|
|
16
17
|
discoverCharacteristicsPromise = {};
|
|
17
18
|
onErrorHandler = this.onPeripheralError.bind(this);
|
|
@@ -155,8 +156,26 @@ export class BlePeripheral {
|
|
|
155
156
|
const promise = this.discoverServicesPromise;
|
|
156
157
|
const res = await promise;
|
|
157
158
|
sleep(0).then(() => { delete this.discoverServicesPromise; });
|
|
159
|
+
const isComplete = this.checkAnnouncedServices(res);
|
|
160
|
+
this.serviceDiscoveryIncomplete = !isComplete;
|
|
161
|
+
if (!isComplete) {
|
|
162
|
+
const { name, address } = this.getInfo();
|
|
163
|
+
this.logEvent({ message: 'service data incomplete - disconnecting', name, address,
|
|
164
|
+
announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
|
|
165
|
+
}
|
|
158
166
|
return res;
|
|
159
167
|
}
|
|
168
|
+
checkAnnouncedServices(discovered) {
|
|
169
|
+
const announced = this.getAnnouncedServices().map(x => beautifyUUID(x));
|
|
170
|
+
const toBeChecked = discovered.map(x => beautifyUUID(x));
|
|
171
|
+
const cntAnnounced = announced.length;
|
|
172
|
+
let cntVerified = 0;
|
|
173
|
+
for (const s of announced) {
|
|
174
|
+
if (toBeChecked.includes(s))
|
|
175
|
+
cntVerified++;
|
|
176
|
+
}
|
|
177
|
+
return cntAnnounced === cntVerified;
|
|
178
|
+
}
|
|
160
179
|
async _discoverServices() {
|
|
161
180
|
if (!this.getPeripheral())
|
|
162
181
|
return [];
|
|
@@ -306,6 +325,14 @@ export class BlePeripheral {
|
|
|
306
325
|
}
|
|
307
326
|
catch { }
|
|
308
327
|
}
|
|
328
|
+
if (this.serviceDiscoveryIncomplete) {
|
|
329
|
+
this.logEvent({ message: 'peripheral subscribe selected failed', uuids, reason: 'service discovery incomplete',
|
|
330
|
+
announced: this.getAnnouncedServices(), discovered: this.getDiscoveredServices() });
|
|
331
|
+
this.discoveredServiceUUIds = undefined;
|
|
332
|
+
this.serviceDiscoveryIncomplete = false;
|
|
333
|
+
await this.disconnect().catch(() => { });
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
309
336
|
try {
|
|
310
337
|
if (Object.keys(this.characteristics).length === 0) {
|
|
311
338
|
await this.discoverAllCharacteristics();
|
|
@@ -346,6 +373,11 @@ export class BlePeripheral {
|
|
|
346
373
|
const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
347
374
|
const found = [];
|
|
348
375
|
const uuids = [];
|
|
376
|
+
const freshUuids = new Set(res.characteristics.map(c => beautifyUUID(c.uuid)));
|
|
377
|
+
Object.keys(this.characteristics).forEach(existing => {
|
|
378
|
+
if (!freshUuids.has(existing))
|
|
379
|
+
delete this.characteristics[existing];
|
|
380
|
+
});
|
|
349
381
|
res.characteristics.forEach(c => {
|
|
350
382
|
this.characteristics[beautifyUUID(c.uuid)] = c;
|
|
351
383
|
found.push(c.uuid);
|
|
@@ -364,6 +396,12 @@ export class BlePeripheral {
|
|
|
364
396
|
const target = characteristics.map(c => fullUUID(c));
|
|
365
397
|
const res = await this.getPeripheral().discoverSomeServicesAndCharacteristicsAsync([], target);
|
|
366
398
|
const found = [];
|
|
399
|
+
const freshUuids = new Set(res.characteristics.map(c => beautifyUUID(c.uuid)));
|
|
400
|
+
characteristics.forEach(requested => {
|
|
401
|
+
const requestedUuid = beautifyUUID(requested);
|
|
402
|
+
if (!freshUuids.has(requestedUuid))
|
|
403
|
+
delete this.characteristics[requestedUuid];
|
|
404
|
+
});
|
|
367
405
|
res.characteristics.forEach(c => {
|
|
368
406
|
this.characteristics[beautifyUUID(c.uuid)] = c;
|
|
369
407
|
found.push(c.uuid);
|
|
@@ -433,25 +471,61 @@ export class BlePeripheral {
|
|
|
433
471
|
return Promise.reject(new Error('characteristic not found: ' + characteristicUUID));
|
|
434
472
|
}
|
|
435
473
|
}
|
|
474
|
+
const signal = options?.signal;
|
|
436
475
|
return new Promise((resolve, reject) => {
|
|
476
|
+
let settled = false;
|
|
477
|
+
let onData;
|
|
478
|
+
const cleanup = () => {
|
|
479
|
+
if (onData) {
|
|
480
|
+
c.off('data', onData);
|
|
481
|
+
onData = undefined;
|
|
482
|
+
}
|
|
483
|
+
signal?.removeEventListener('abort', onAbort);
|
|
484
|
+
};
|
|
485
|
+
const settleResolve = (result) => {
|
|
486
|
+
if (settled)
|
|
487
|
+
return;
|
|
488
|
+
settled = true;
|
|
489
|
+
cleanup();
|
|
490
|
+
resolve(result);
|
|
491
|
+
};
|
|
492
|
+
const settleReject = (err) => {
|
|
493
|
+
if (settled)
|
|
494
|
+
return;
|
|
495
|
+
settled = true;
|
|
496
|
+
cleanup();
|
|
497
|
+
reject(err);
|
|
498
|
+
};
|
|
499
|
+
const onAbort = () => {
|
|
500
|
+
settleReject(new Error('aborted'));
|
|
501
|
+
};
|
|
502
|
+
if (signal) {
|
|
503
|
+
if (signal.aborted) {
|
|
504
|
+
settleReject(new Error('aborted'));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
508
|
+
}
|
|
437
509
|
const write = () => {
|
|
438
|
-
if (this.disconnecting || !this.connected)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
510
|
+
if (this.disconnecting || !this.connected) {
|
|
511
|
+
settleResolve(Buffer.from([]));
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
onData = (responseData) => {
|
|
515
|
+
settleResolve(responseData);
|
|
516
|
+
};
|
|
517
|
+
c.on('data', onData);
|
|
444
518
|
this.logEvent({ message: 'write request', characteristic: uuid, data: data.toString('hex'), withoutResponse: options?.withoutResponse === true });
|
|
445
519
|
c.write(data, options?.withoutResponse === true, (err) => {
|
|
446
520
|
if (err)
|
|
447
|
-
|
|
521
|
+
settleReject(err);
|
|
448
522
|
});
|
|
449
523
|
if (options?.withoutResponse) {
|
|
450
|
-
|
|
524
|
+
settleResolve(Buffer.from([]));
|
|
451
525
|
}
|
|
452
526
|
};
|
|
453
527
|
if (!options?.withoutResponse) {
|
|
454
|
-
this.subscribe(characteristicUUID, null).then(
|
|
528
|
+
this.subscribe(characteristicUUID, null).then(() => {
|
|
455
529
|
write();
|
|
456
530
|
});
|
|
457
531
|
}
|
|
@@ -220,24 +220,33 @@ export default class BleFmAdapter extends BleAdapter {
|
|
|
220
220
|
let hasControl = false;
|
|
221
221
|
let tryCnt = 0;
|
|
222
222
|
const sensor = this.getSensor();
|
|
223
|
+
let cancelled = false;
|
|
224
|
+
const controlAbort = new AbortController();
|
|
225
|
+
const abandon = () => {
|
|
226
|
+
if (cancelled)
|
|
227
|
+
return;
|
|
228
|
+
cancelled = true;
|
|
229
|
+
controlAbort.abort();
|
|
230
|
+
};
|
|
223
231
|
const controlPromise = new Promise((resolve) => {
|
|
224
232
|
this.startTask.notifyOnStop(() => {
|
|
233
|
+
abandon();
|
|
225
234
|
resolve(false);
|
|
226
235
|
});
|
|
227
236
|
const waitUntilControl = async () => {
|
|
228
237
|
if (this.supportsVirtualShifting()) {
|
|
229
238
|
await this.initVirtualShifting();
|
|
230
239
|
}
|
|
231
|
-
while (!hasControl && this.isStarting()) {
|
|
240
|
+
while (!hasControl && !cancelled && this.isStarting()) {
|
|
232
241
|
if (tryCnt++ === 0) {
|
|
233
242
|
this.logEvent({ message: 'requesting control', device: this.getName(), interface: this.getInterface() });
|
|
234
243
|
}
|
|
235
|
-
hasControl = await sensor.requestControl();
|
|
244
|
+
hasControl = await sensor.requestControl(controlAbort.signal);
|
|
236
245
|
if (hasControl) {
|
|
237
246
|
this.logEvent({ message: 'control granted', device: this.getName(), interface: this.getInterface() });
|
|
238
247
|
resolve(this.isStarting());
|
|
239
248
|
}
|
|
240
|
-
else {
|
|
249
|
+
else if (!cancelled) {
|
|
241
250
|
await sleep(this.requestControlRetryDelay);
|
|
242
251
|
}
|
|
243
252
|
}
|
|
@@ -248,10 +257,12 @@ export default class BleFmAdapter extends BleAdapter {
|
|
|
248
257
|
timeout: this.requestControlTimeout,
|
|
249
258
|
name: 'establishControl',
|
|
250
259
|
errorOnTimeout: false,
|
|
251
|
-
log: this.logEvent.bind(this)
|
|
260
|
+
log: this.logEvent.bind(this),
|
|
261
|
+
onCancel: abandon
|
|
252
262
|
});
|
|
253
263
|
const granted = await waitTask.run();
|
|
254
264
|
if (!granted && this.isStarting()) {
|
|
265
|
+
abandon();
|
|
255
266
|
this.logEvent({ message: 'could not establish control', device: this.getName(), interface: this.getInterface() });
|
|
256
267
|
throw new Error('could not establish control');
|
|
257
268
|
}
|
package/lib/esm/ble/fm/sensor.js
CHANGED
|
@@ -103,7 +103,7 @@ export default class BleFitnessMachineDevice extends TBleSensor {
|
|
|
103
103
|
this.hasControl = false;
|
|
104
104
|
this.ftmsServiceDataAttempts = 0;
|
|
105
105
|
}
|
|
106
|
-
async requestControl() {
|
|
106
|
+
async requestControl(signal) {
|
|
107
107
|
if (this.hasControl) {
|
|
108
108
|
return true;
|
|
109
109
|
}
|
|
@@ -113,7 +113,7 @@ export default class BleFitnessMachineDevice extends TBleSensor {
|
|
|
113
113
|
this.isCheckingControl = true;
|
|
114
114
|
const data = Buffer.alloc(1);
|
|
115
115
|
data.writeUInt8(0, 0);
|
|
116
|
-
const res = await this.writeFtmsMessage(0, data, { timeout: 5000 });
|
|
116
|
+
const res = await this.writeFtmsMessage(0, data, { timeout: 5000, signal });
|
|
117
117
|
if (res === 1) {
|
|
118
118
|
this.hasControl = true;
|
|
119
119
|
}
|
|
@@ -620,9 +620,15 @@ export default class BleFitnessMachineDevice extends TBleSensor {
|
|
|
620
620
|
let res;
|
|
621
621
|
let tsStart = Date.now();
|
|
622
622
|
if (props?.timeout) {
|
|
623
|
-
|
|
623
|
+
const internalAbort = new AbortController();
|
|
624
|
+
const combined = new AbortController();
|
|
625
|
+
props.signal?.addEventListener('abort', () => combined.abort(), { once: true });
|
|
626
|
+
internalAbort.signal.addEventListener('abort', () => combined.abort(), { once: true });
|
|
627
|
+
const signal = combined.signal;
|
|
628
|
+
res = await new InteruptableTask(this.write(FTMS_CP, data, { ...props, signal }), {
|
|
624
629
|
timeout: props.timeout ?? 800,
|
|
625
|
-
errorOnTimeout: true
|
|
630
|
+
errorOnTimeout: true,
|
|
631
|
+
onCancel: () => internalAbort.abort()
|
|
626
632
|
}).run();
|
|
627
633
|
}
|
|
628
634
|
else {
|
package/lib/esm/utils/task.js
CHANGED
|
@@ -7,6 +7,7 @@ export class InteruptableTask {
|
|
|
7
7
|
internalEvents = new EventEmitter();
|
|
8
8
|
promise;
|
|
9
9
|
onStopNotifiers = [];
|
|
10
|
+
cancelNotified = false;
|
|
10
11
|
constructor(promise, props) {
|
|
11
12
|
this.state = (props?.state ?? {});
|
|
12
13
|
this.props = props;
|
|
@@ -17,6 +18,9 @@ export class InteruptableTask {
|
|
|
17
18
|
getPromise() {
|
|
18
19
|
return this.internalState.promise;
|
|
19
20
|
}
|
|
21
|
+
getUnderlyingPromise() {
|
|
22
|
+
return this.promise;
|
|
23
|
+
}
|
|
20
24
|
getState() {
|
|
21
25
|
return this.state;
|
|
22
26
|
}
|
|
@@ -37,6 +41,7 @@ export class InteruptableTask {
|
|
|
37
41
|
const { timeout } = this.props;
|
|
38
42
|
if (timeout) {
|
|
39
43
|
this.internalState.tsTimeout = this.internalState.tsStart + timeout;
|
|
44
|
+
this.internalState.timeoutDuration = timeout;
|
|
40
45
|
this.internalState.onTimeout = this.onTimeout.bind(this);
|
|
41
46
|
this.internalState.timeout = setTimeout(() => { this.internalEvents.emit('timeout'); }, timeout);
|
|
42
47
|
this.internalEvents.on('timeout', this.internalState.onTimeout);
|
|
@@ -50,6 +55,7 @@ export class InteruptableTask {
|
|
|
50
55
|
this.sendStopNotification();
|
|
51
56
|
if (this.getState().result === 'completed' || this.getState().result === 'error')
|
|
52
57
|
return;
|
|
58
|
+
this.notifyCancel();
|
|
53
59
|
this.getState().result = 'stopped';
|
|
54
60
|
if (this.props.onDone)
|
|
55
61
|
resolve(this.props.onDone(this.getState()));
|
|
@@ -94,8 +100,9 @@ export class InteruptableTask {
|
|
|
94
100
|
if (!this.internalState.timeout)
|
|
95
101
|
return;
|
|
96
102
|
const message = this.props.name ? `${this.props.name} timeout` : 'timeout';
|
|
97
|
-
this.logEvent({ message, active: this.isRunning() });
|
|
103
|
+
this.logEvent({ message, active: this.isRunning(), duration: this.internalState?.timeoutDuration });
|
|
98
104
|
this.clearTimeout();
|
|
105
|
+
this.notifyCancel();
|
|
99
106
|
this.getState().result = 'timeout';
|
|
100
107
|
const resolve = this.internalState.onDone;
|
|
101
108
|
const reject = this.internalState.onError;
|
|
@@ -108,6 +115,12 @@ export class InteruptableTask {
|
|
|
108
115
|
else
|
|
109
116
|
resolve(null);
|
|
110
117
|
}
|
|
118
|
+
notifyCancel() {
|
|
119
|
+
if (this.cancelNotified)
|
|
120
|
+
return;
|
|
121
|
+
this.cancelNotified = true;
|
|
122
|
+
this.props.onCancel?.();
|
|
123
|
+
}
|
|
111
124
|
sendStopNotification() {
|
|
112
125
|
this.onStopNotifiers.forEach((cb) => {
|
|
113
126
|
if (typeof cb === 'function')
|
|
@@ -15,6 +15,8 @@ export default class BleAdapter<TDeviceData extends BleDeviceData, TDevice exten
|
|
|
15
15
|
protected onDeviceDisconnectHandler: any;
|
|
16
16
|
protected onDisconnectDoneHandler: any;
|
|
17
17
|
protected startTask: InteruptableTask<TaskState, boolean>;
|
|
18
|
+
protected startPromise?: Promise<boolean>;
|
|
19
|
+
protected stopPromise?: Promise<boolean>;
|
|
18
20
|
constructor(settings: BleDeviceSettings, props?: DeviceProperties);
|
|
19
21
|
getUniqueName(): string;
|
|
20
22
|
connect(): Promise<boolean>;
|
|
@@ -43,6 +45,7 @@ export default class BleAdapter<TDeviceData extends BleDeviceData, TDevice exten
|
|
|
43
45
|
getDefaultStartupTimeout(): number;
|
|
44
46
|
startPreChecks(props: BleStartProperties): Promise<'done' | 'connected' | 'connection-failed'>;
|
|
45
47
|
start(startProps?: BleStartProperties): Promise<boolean>;
|
|
48
|
+
protected runStartTask(ble: IBleInterface<any>): Promise<boolean>;
|
|
46
49
|
protected isStarting(): boolean;
|
|
47
50
|
protected hasData(): boolean;
|
|
48
51
|
protected waitForInitialData(startupTimeout: any): Promise<void>;
|
|
@@ -55,6 +58,7 @@ export default class BleAdapter<TDeviceData extends BleDeviceData, TDevice exten
|
|
|
55
58
|
protected onDisconnectDone(): Promise<void>;
|
|
56
59
|
restart(pause?: number): Promise<boolean>;
|
|
57
60
|
stop(): Promise<boolean>;
|
|
61
|
+
protected runStop(): Promise<boolean>;
|
|
58
62
|
pause(): Promise<boolean>;
|
|
59
63
|
resume(): Promise<boolean>;
|
|
60
64
|
protected getBle(): IBleInterface<any>;
|
|
@@ -14,6 +14,7 @@ export declare class BlePeripheral implements IBlePeripheral {
|
|
|
14
14
|
protected disconnecting: boolean;
|
|
15
15
|
protected disconnectedSignalled: boolean;
|
|
16
16
|
protected discoveredServiceUUIds: Array<string> | undefined;
|
|
17
|
+
protected serviceDiscoveryIncomplete: boolean;
|
|
17
18
|
protected discoverServicesPromise: Promise<string[]> | undefined;
|
|
18
19
|
protected discoverCharacteristicsPromise: Record<string, Promise<BleCharacteristic[]> | undefined>;
|
|
19
20
|
protected onErrorHandler: any;
|
|
@@ -29,15 +30,16 @@ export declare class BlePeripheral implements IBlePeripheral {
|
|
|
29
30
|
isConnected(): boolean;
|
|
30
31
|
isConnecting(): boolean;
|
|
31
32
|
onDisconnect(callback: () => void): void;
|
|
32
|
-
getManufacturerData(): Buffer
|
|
33
|
+
getManufacturerData(): Buffer;
|
|
33
34
|
getServiceData(uuid: string): Buffer | undefined;
|
|
34
35
|
protected onPeripheralDisconnect(): Promise<void>;
|
|
35
36
|
protected onPeripheralError(err: Error): void;
|
|
36
37
|
discoverServices(): Promise<string[]>;
|
|
38
|
+
protected checkAnnouncedServices(discovered: string[]): boolean;
|
|
37
39
|
protected _discoverServices(): Promise<string[]>;
|
|
38
40
|
discoverCharacteristics(serviceUUID: string): Promise<BleCharacteristic[]>;
|
|
39
41
|
protected _discoverCharacteristics(serviceUUID: string): Promise<BleCharacteristic[]>;
|
|
40
|
-
subscribe(characteristicUUID: string, callback: (characteristicUuid: string, data: Buffer, isNotify?:
|
|
42
|
+
subscribe(characteristicUUID: string, callback: (characteristicUuid: string, data: Buffer, isNotify?: boolean) => void): Promise<boolean>;
|
|
41
43
|
unsubscribe(characteristicUUID: string): Promise<boolean>;
|
|
42
44
|
subscribeSelected(characteristics: string[], callback: (characteristicUuid: string, data: Buffer, isNotify?: boolean) => void): Promise<boolean>;
|
|
43
45
|
discoverAllCharacteristics(): Promise<string[]>;
|
|
@@ -35,7 +35,7 @@ export default class BleFitnessMachineDevice extends TBleSensor {
|
|
|
35
35
|
getWindSpeed(): number;
|
|
36
36
|
getSupportedSports(): Array<Sport>;
|
|
37
37
|
protected onDisconnect(): void;
|
|
38
|
-
requestControl(): Promise<boolean>;
|
|
38
|
+
requestControl(signal?: AbortSignal): Promise<boolean>;
|
|
39
39
|
setTargetPower(power: number): Promise<boolean>;
|
|
40
40
|
setTargetResistanceLevel(resistanceLevel: number): Promise<boolean>;
|
|
41
41
|
setSlope(slope: number): Promise<boolean>;
|
package/lib/types/ble/types.d.ts
CHANGED
|
@@ -13,10 +13,12 @@ export interface TaskProps<T, P> {
|
|
|
13
13
|
errorOnTimeout?: boolean;
|
|
14
14
|
log?: (event: any) => void;
|
|
15
15
|
onDone?: (state: T) => P;
|
|
16
|
+
onCancel?: () => void;
|
|
16
17
|
}
|
|
17
18
|
interface InternalTaskState<P> {
|
|
18
19
|
tsStart?: number;
|
|
19
20
|
tsTimeout?: number;
|
|
21
|
+
timeoutDuration?: number;
|
|
20
22
|
isRunning: boolean;
|
|
21
23
|
timeout?: NodeJS.Timeout;
|
|
22
24
|
promise?: Promise<P>;
|
|
@@ -31,8 +33,10 @@ export declare class InteruptableTask<T extends TaskState, P> {
|
|
|
31
33
|
protected internalEvents: EventEmitter<any>;
|
|
32
34
|
protected promise?: Promise<P>;
|
|
33
35
|
protected onStopNotifiers: Array<() => void>;
|
|
36
|
+
protected cancelNotified: boolean;
|
|
34
37
|
constructor(promise: Promise<any>, props?: TaskProps<T, P>);
|
|
35
38
|
getPromise(): Promise<P>;
|
|
39
|
+
getUnderlyingPromise(): Promise<any> | undefined;
|
|
36
40
|
getState(): T;
|
|
37
41
|
run(): Promise<P>;
|
|
38
42
|
notifyOnStop(cb: () => void): void;
|
|
@@ -41,6 +45,7 @@ export declare class InteruptableTask<T extends TaskState, P> {
|
|
|
41
45
|
isRunning(): boolean;
|
|
42
46
|
protected clearTimeout(): void;
|
|
43
47
|
protected onTimeout(): void;
|
|
48
|
+
protected notifyCancel(): void;
|
|
44
49
|
protected sendStopNotification(): void;
|
|
45
50
|
protected logEvent(event: any): void;
|
|
46
51
|
}
|