incyclist-devices 2.1.1 → 2.1.2

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.
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
2
  import { IChannel, ISensor, Profile } from 'incyclist-ant-plus';
3
- import AntInterface from './ant-interface';
3
+ import AntInterface from './interface';
4
4
  import IncyclistDevice from '../../base/adpater';
5
- import { AntDeviceProperties, AntDeviceSettings, LegacyProfile, BaseDeviceData } from '../types';
5
+ import { AntDeviceProperties, AntDeviceSettings, LegacyProfile, BaseDeviceData, AdapterStartStatus } from '../types';
6
6
  import { IAdapter, IncyclistAdapterData, IncyclistBikeData } from '../../types';
7
7
  export default class AntAdapter<TDeviceData extends BaseDeviceData> extends IncyclistDevice<AntDeviceProperties> {
8
8
  sensor: ISensor;
@@ -18,17 +18,20 @@ export default class AntAdapter<TDeviceData extends BaseDeviceData> extends Incy
18
18
  weight?: number;
19
19
  };
20
20
  onDataFn: (data: IncyclistAdapterData) => void;
21
- startupRetryPause: number;
22
21
  protected ivDataTimeout: NodeJS.Timeout;
23
22
  protected lastDataTS: number;
24
23
  protected dataMsgCount: number;
25
24
  protected ivWaitForData: NodeJS.Timeout;
26
25
  protected promiseWaitForData: Promise<boolean>;
26
+ protected sensorConnected: boolean;
27
+ protected startStatus: AdapterStartStatus;
28
+ protected startupRetryPause: number;
27
29
  constructor(settings: AntDeviceSettings, props?: AntDeviceProperties);
28
30
  getProfileName(): Profile;
29
31
  getLegacyProfileName(): LegacyProfile;
30
32
  createSensor(settings: AntDeviceSettings): ISensor;
31
33
  isEqual(settings: AntDeviceSettings): boolean;
34
+ getDefaultReconnectDelay(): number;
32
35
  connect(): Promise<boolean>;
33
36
  close(): Promise<boolean>;
34
37
  resetData(): void;
@@ -48,15 +51,19 @@ export default class AntAdapter<TDeviceData extends BaseDeviceData> extends Incy
48
51
  getInterface(): string;
49
52
  getProfile(): Profile;
50
53
  getLogData(data: any, excludeList: any): any;
51
- triggerTimeoutCheck(): void;
52
- startDataTimeoutCheck(): void;
53
- stopDataTimeoutCheck(): void;
54
54
  check(): Promise<boolean>;
55
55
  checkCapabilities(): Promise<void>;
56
56
  initControl(): Promise<void>;
57
57
  getDefaultStartupTimeout(): number;
58
58
  sendUpdate(request: any): void;
59
+ startPreChecks(props: AntDeviceProperties): Promise<'done' | 'connected' | 'connection-failed'>;
60
+ resetStartStatus(): void;
61
+ isStartSuccess(): boolean;
62
+ reportStartStatus(): boolean;
63
+ protected waitForInitialData(startupTimeout: any): Promise<void>;
64
+ protected initSensor(props: any): Promise<boolean>;
59
65
  start(props?: AntDeviceProperties): Promise<boolean>;
60
66
  stop(): Promise<boolean>;
61
67
  startSensor(): Promise<boolean>;
68
+ stopSensor(): Promise<void>;
62
69
  }
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- const ant_interface_1 = __importDefault(require("./ant-interface"));
15
+ const interface_1 = __importDefault(require("./interface"));
16
16
  const adpater_1 = __importDefault(require("../../base/adpater"));
17
17
  const types_1 = require("../types");
18
18
  const types_2 = require("../../types");
@@ -22,6 +22,7 @@ const consts_1 = require("../consts");
22
22
  const sensor_factory_1 = __importDefault(require("../factories/sensor-factory"));
23
23
  const gd_eventlog_1 = require("gd-eventlog");
24
24
  const INTERFACE_NAME = 'ant';
25
+ const MAX_RETRIES = 3;
25
26
  class AntAdapter extends adpater_1.default {
26
27
  constructor(settings, props) {
27
28
  super(settings, props);
@@ -37,6 +38,7 @@ class AntAdapter extends adpater_1.default {
37
38
  if (this.settings.interface !== 'ant')
38
39
  throw new Error('Incorrect interface');
39
40
  this.sensor = this.createSensor(settings);
41
+ this.sensorConnected = false;
40
42
  this.deviceData = {
41
43
  DeviceID: Number(settings.deviceID)
42
44
  };
@@ -45,7 +47,7 @@ class AntAdapter extends adpater_1.default {
45
47
  this.logger = new gd_eventlog_1.EventLogger(`Ant+${profile}`);
46
48
  this.updateFrequency = consts_1.DEFAULT_UPDATE_FREQUENCY;
47
49
  this.channel = undefined;
48
- this.ant = ant_interface_1.default.getInstance();
50
+ this.ant = interface_1.default.getInstance();
49
51
  }
50
52
  getProfileName() {
51
53
  const C = this.constructor;
@@ -66,9 +68,12 @@ class AntAdapter extends adpater_1.default {
66
68
  return false;
67
69
  return true;
68
70
  }
71
+ getDefaultReconnectDelay() {
72
+ return this.startupRetryPause;
73
+ }
69
74
  connect() {
70
75
  return __awaiter(this, void 0, void 0, function* () {
71
- const connected = yield ant_interface_1.default.getInstance().connect();
76
+ const connected = yield interface_1.default.getInstance().connect();
72
77
  return connected;
73
78
  });
74
79
  }
@@ -113,7 +118,6 @@ class AntAdapter extends adpater_1.default {
113
118
  }
114
119
  if (!this.started || this.isStopped())
115
120
  return;
116
- this.triggerTimeoutCheck();
117
121
  if (!this.canEmitData())
118
122
  return;
119
123
  const logData = this.getLogData(deviceData, ['PairedDevices', 'RawData']);
@@ -158,16 +162,13 @@ class AntAdapter extends adpater_1.default {
158
162
  }
159
163
  const tsStart = Date.now();
160
164
  if (this.promiseWaitForData) {
165
+ let hasData = false;
161
166
  try {
162
- const hasData = yield (0, utils_1.runWithTimeout)(this.promiseWaitForData, timeout);
163
- if (hasData || Date.now() - tsStart > timeout)
164
- return hasData;
165
- }
166
- catch (_a) {
167
- timeout -= (Date.now() - tsStart);
168
- if (timeout < 0)
169
- return false;
167
+ hasData = yield this.promiseWaitForData;
170
168
  }
169
+ catch (_a) { }
170
+ if (hasData || Date.now() > tsStart + timeout)
171
+ return hasData;
171
172
  }
172
173
  try {
173
174
  this.promiseWaitForData = (0, utils_1.runWithTimeout)(this._wait(), timeout);
@@ -226,30 +227,6 @@ class AntAdapter extends adpater_1.default {
226
227
  });
227
228
  return logData;
228
229
  }
229
- triggerTimeoutCheck() {
230
- if (!this.ivDataTimeout && this.dataMsgCount > 0) {
231
- this.startDataTimeoutCheck();
232
- }
233
- }
234
- startDataTimeoutCheck() {
235
- if (this.ivDataTimeout)
236
- return;
237
- this.ivDataTimeout = setInterval(() => {
238
- if (!this.lastDataTS)
239
- return;
240
- if (this.lastDataTS + consts_1.NO_DATA_TIMEOUT < Date.now()) {
241
- this.emit('disconnected', Date.now() - this.lastDataTS);
242
- }
243
- }, 1000);
244
- }
245
- stopDataTimeoutCheck() {
246
- if (!this.ivDataTimeout)
247
- return;
248
- clearInterval(this.ivDataTimeout);
249
- this.ivDataTimeout = undefined;
250
- this.lastDataTS = undefined;
251
- this.dataMsgCount = 0;
252
- }
253
230
  check() {
254
231
  return __awaiter(this, void 0, void 0, function* () {
255
232
  try {
@@ -283,67 +260,158 @@ class AntAdapter extends adpater_1.default {
283
260
  else
284
261
  throw new Error('method not implemented');
285
262
  }
286
- start(props = {}) {
263
+ startPreChecks(props) {
287
264
  return __awaiter(this, void 0, void 0, function* () {
288
- if (this.started && !this.stopped) {
289
- if (this.paused)
290
- this.resume();
291
- return true;
292
- }
265
+ const wasPaused = this.paused;
266
+ const wasStopped = this.stopped;
293
267
  this.stopped = false;
268
+ if (wasPaused)
269
+ this.resume();
270
+ if (this.started && !wasPaused && !wasStopped) {
271
+ return 'done';
272
+ }
273
+ if (this.started && wasPaused) {
274
+ return 'done';
275
+ }
294
276
  const connected = yield this.connect();
295
277
  if (!connected)
278
+ return 'connection-failed';
279
+ return 'connected';
280
+ });
281
+ }
282
+ resetStartStatus() {
283
+ this.startStatus = { timeout: false, hasData: false, sensorStarted: false };
284
+ }
285
+ isStartSuccess() {
286
+ const { timeout, hasData, sensorStarted, controlInitialized, userInitialized, interrupted } = this.startStatus;
287
+ if (interrupted)
288
+ return false;
289
+ if (this.hasCapability(types_2.IncyclistCapability.Control))
290
+ return sensorStarted && hasData && userInitialized && controlInitialized && !timeout;
291
+ else
292
+ return sensorStarted && hasData && !timeout;
293
+ }
294
+ reportStartStatus() {
295
+ const success = this.isStartSuccess();
296
+ if (success) {
297
+ this.logEvent({ message: 'start device success' });
298
+ this.started = true;
299
+ this.paused = false;
300
+ return true;
301
+ }
302
+ else {
303
+ this.started = false;
304
+ const { sensorStarted, hasData, interrupted } = this.startStatus;
305
+ if (interrupted)
306
+ return;
307
+ if (!sensorStarted) {
308
+ this.logEvent({ message: 'start device failed', reason: 'could not connect' });
309
+ throw new Error('could not start device, reason:could not connect');
310
+ }
311
+ else if (!hasData) {
312
+ this.logEvent({ message: 'start device failed', reason: 'no data received' });
313
+ throw new Error('could not start device, reason:no data received');
314
+ }
315
+ else {
316
+ this.logEvent({ message: 'start device failed', reason: 'could not send FE commands' });
317
+ throw new Error('could not start device, reason:could not send FE commands');
318
+ }
319
+ }
320
+ }
321
+ waitForInitialData(startupTimeout) {
322
+ return __awaiter(this, void 0, void 0, function* () {
323
+ const { sensorStarted, hasData, timeout } = this.startStatus;
324
+ if ((sensorStarted && hasData) || !sensorStarted || timeout)
325
+ return;
326
+ this.logEvent({ message: 'wait for sensor data', });
327
+ this.startStatus.hasData = yield this.waitForData(startupTimeout);
328
+ if (this.startStatus.hasData)
329
+ this.logEvent({ message: 'sensor data received', });
330
+ });
331
+ }
332
+ initSensor(props) {
333
+ return __awaiter(this, void 0, void 0, function* () {
334
+ this.startStatus.sensorStarted = this.sensorConnected;
335
+ if (this.startStatus.sensorStarted || this.startStatus.sensorStarted)
336
+ return;
337
+ this.logEvent({ message: 'start sensor', props });
338
+ try {
339
+ this.sensorConnected = yield this.startSensor();
340
+ if (this.sensorConnected) {
341
+ this.logEvent({ message: 'sensor started', props });
342
+ this.startStatus.sensorStarted = true;
343
+ }
344
+ }
345
+ catch (err) {
346
+ this.logEvent({ message: 'start sensor failed', reason: err.message, props });
347
+ }
348
+ });
349
+ }
350
+ start(props = {}) {
351
+ return __awaiter(this, void 0, void 0, function* () {
352
+ const preCheckResult = yield this.startPreChecks(props);
353
+ if (preCheckResult === 'done')
354
+ return this.started;
355
+ if (preCheckResult === 'connection-failed')
296
356
  throw new Error(`could not start device, reason:could not connect`);
297
- return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
298
- this.resetData();
299
- this.stopped = false;
300
- this.resume();
301
- const { startupTimeout = this.getDefaultStartupTimeout() } = props;
302
- let to = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
357
+ this.logEvent({ message: 'starting device', props, isStarted: this.started });
358
+ this.resetStartStatus();
359
+ this.resetData();
360
+ const { startupTimeout = this.getDefaultStartupTimeout() } = props || {};
361
+ const retryDelay = this.getDefaultReconnectDelay();
362
+ const totalTimeout = Math.min(startupTimeout + 10000, (startupTimeout + retryDelay) * MAX_RETRIES);
363
+ const doStart = () => __awaiter(this, void 0, void 0, function* () {
364
+ let success = false;
365
+ let retry = 0;
366
+ while (!success && retry < MAX_RETRIES && !this.startStatus.timeout && !this.startStatus.interrupted) {
303
367
  try {
304
- yield this.stop();
368
+ retry++;
369
+ yield this.initSensor(props);
370
+ yield this.waitForInitialData(startupTimeout);
371
+ yield this.checkCapabilities();
372
+ if (this.hasCapability(types_2.IncyclistCapability.Control))
373
+ yield this.initControl();
374
+ if (!this.startStatus.hasData) {
375
+ yield this.stopSensor();
376
+ yield (0, utils_1.sleep)(retryDelay);
377
+ continue;
378
+ }
379
+ success = this.isStartSuccess();
380
+ }
381
+ catch (err) {
382
+ this.logEvent({ message: 'error', fn: 'start#doStart', error: err.message, stack: err.stack });
305
383
  }
306
- catch (_a) { }
307
- this.started = false;
308
- reject(new Error(`could not start device, reason:timeout`));
309
- }), startupTimeout);
310
- let started = false;
311
- do {
312
- started = yield this.ant.startSensor(this.sensor, (data) => {
313
- this.onDeviceData(data);
314
- });
315
- if (!started)
316
- yield (0, utils_1.sleep)(this.startupRetryPause);
317
- } while (!started);
318
- try {
319
- this.logEvent({ message: 'wait for sensor data', });
320
- const hasData = yield this.waitForData(startupTimeout - 100);
321
- if (!hasData)
322
- throw new Error('timeout');
323
- this.logEvent({ message: 'sensor data received', });
324
- yield this.checkCapabilities();
325
- if (this.hasCapability(types_2.IncyclistCapability.Control))
326
- yield this.initControl();
327
- this.started = true;
328
- if (to)
329
- clearTimeout(to);
330
- resolve(true);
331
384
  }
332
- catch (err) {
385
+ this.reportStartStatus();
386
+ return this.started;
387
+ });
388
+ try {
389
+ yield (0, utils_1.runWithTimeout)(doStart(), totalTimeout);
390
+ }
391
+ catch (err) {
392
+ if (err.message === 'Timeout') {
393
+ this.started = false;
394
+ this.startStatus.timeout = true;
395
+ throw new Error(`could not start device, reason:timeout`);
333
396
  }
334
- }));
397
+ throw err;
398
+ }
399
+ return true;
335
400
  });
336
401
  }
337
402
  stop() {
338
403
  return __awaiter(this, void 0, void 0, function* () {
339
404
  let stopped;
405
+ this.promiseWaitForData = null;
406
+ if (this.startStatus)
407
+ this.startStatus.interrupted = true;
340
408
  try {
341
- this.stopDataTimeoutCheck();
342
409
  stopped = yield this.ant.stopSensor(this.sensor);
343
410
  }
344
411
  catch (err) {
345
412
  this.logEvent({ message: 'stop sensor failed', reason: err.message });
346
413
  }
414
+ this.sensorConnected = false;
347
415
  this.started = false;
348
416
  this.stopped = true;
349
417
  this.paused = false;
@@ -356,5 +424,16 @@ class AntAdapter extends adpater_1.default {
356
424
  return this.ant.startSensor(this.sensor, this.onDeviceData.bind(this));
357
425
  });
358
426
  }
427
+ stopSensor() {
428
+ return __awaiter(this, void 0, void 0, function* () {
429
+ if (!this.sensorConnected)
430
+ return;
431
+ try {
432
+ yield yield this.ant.stopSensor(this.sensor);
433
+ this.sensorConnected = false;
434
+ }
435
+ catch (_a) { }
436
+ });
437
+ }
359
438
  }
360
439
  exports.default = AntAdapter;
@@ -0,0 +1,46 @@
1
+ /// <reference types="node" />
2
+ import EventEmitter from "events";
3
+ import { EventLogger } from "gd-eventlog";
4
+ import { IAntDevice, IChannel, ISensor } from "incyclist-ant-plus";
5
+ import { AntDeviceSettings, AntScanProps, AntInterfaceProps } from "../types";
6
+ import { IncyclistInterface } from "../../types";
7
+ import AntDeviceBinding from "./binding";
8
+ export default class AntInterface extends EventEmitter implements IncyclistInterface {
9
+ static _instance: AntInterface;
10
+ static INTERFACE_NAME: string;
11
+ static getInstance(props?: AntInterfaceProps): AntInterface;
12
+ static hasInstance(): boolean;
13
+ protected logger: EventLogger;
14
+ protected device: IAntDevice;
15
+ protected Binding: typeof AntDeviceBinding;
16
+ protected connected: boolean;
17
+ protected connectPromise: Promise<boolean>;
18
+ protected scanPromise: Promise<AntDeviceSettings[]>;
19
+ protected activeScan: {
20
+ emitter: EventEmitter;
21
+ channel?: IChannel;
22
+ };
23
+ protected props: AntInterfaceProps;
24
+ protected logEnabled: boolean;
25
+ constructor(props: AntInterfaceProps);
26
+ getName(): string;
27
+ getBinding(): typeof AntDeviceBinding;
28
+ setBinding(binding: typeof AntDeviceBinding): void;
29
+ getLogger(): EventLogger;
30
+ setLogger(logger: EventLogger): void;
31
+ enableLogging(): void;
32
+ disableLogging(): void;
33
+ logEvent(event: any): void;
34
+ isConnected(): boolean;
35
+ connect(): Promise<boolean>;
36
+ disconnect(): Promise<boolean>;
37
+ onError(profile: any, error: any): void;
38
+ onData(profile: any, id: any, data: any, tag: any): void;
39
+ getReconnectPause(): number;
40
+ scannerWaitForConnection(): Promise<void>;
41
+ scan(props?: AntScanProps): Promise<AntDeviceSettings[]>;
42
+ isScanning(): boolean;
43
+ stopScan(): Promise<boolean>;
44
+ startSensor(sensor: ISensor, onDeviceData: (data: any) => void): Promise<boolean>;
45
+ stopSensor(sensor: ISensor): Promise<boolean>;
46
+ }