incyclist-services 1.4.8 → 1.4.10

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.
@@ -6,6 +6,7 @@ import { IncyclistService } from "../../base/service";
6
6
  export declare class DeviceRideService extends IncyclistService {
7
7
  protected initizialized: boolean;
8
8
  protected adapters: AdapterRideInfo[];
9
+ protected rideAdapters: AdapterRideInfo[];
9
10
  protected startPromises: Promise<boolean>[];
10
11
  protected data: DeviceData;
11
12
  protected simulatorEnforced: boolean;
@@ -15,6 +16,7 @@ export declare class DeviceRideService extends IncyclistService {
15
16
  protected deviceDataHandler: any;
16
17
  protected lazyInitDone: boolean;
17
18
  protected lastDataInfo: Record<string, number>;
19
+ protected reconnectBusy: boolean;
18
20
  constructor();
19
21
  protected waitForInit(): Promise<void>;
20
22
  lazyInit(): Promise<void>;
@@ -33,6 +35,10 @@ export declare class DeviceRideService extends IncyclistService {
33
35
  info: AdapterInfo;
34
36
  }>;
35
37
  startAdapters(adapters: AdapterRideInfo[], startType: 'start' | 'check' | 'pair', props?: RideServiceDeviceProperties): Promise<boolean>;
38
+ protected startSingleAdapter(ai: AdapterRideInfo, duplicates: any, props: any, startType: 'start' | 'check' | 'pair'): Promise<boolean>;
39
+ private initForStart;
40
+ private initForPairing;
41
+ private initCyclingMode;
36
42
  private handleStartRejection;
37
43
  private handleStartFailure;
38
44
  private handleStartSuccess;
@@ -42,11 +48,11 @@ export declare class DeviceRideService extends IncyclistService {
42
48
  stopHealthCheck(ai: AdapterRideInfo): void;
43
49
  prepareReconnect(unhealthy: AdapterRideInfo): Promise<void>;
44
50
  private reconnectInterface;
45
- private finalizeAdaptersAfterRestart;
46
51
  private performInterfaceReconnect;
52
+ protected stopAllAdaptersOnInterface(ifName: string): Promise<void>;
47
53
  private stopAdapters;
48
54
  private reconnectAdapters;
49
- protected stopAfterRestart(unhealthy: AdapterRideInfo): Promise<void>;
55
+ protected stopDuringInterfaceRestart(unhealthy: AdapterRideInfo): Promise<void>;
50
56
  private reconnectSingle;
51
57
  start(props: RideServiceDeviceProperties): Promise<boolean>;
52
58
  startRetry(props: RideServiceDeviceProperties): Promise<boolean>;
@@ -60,7 +66,7 @@ export declare class DeviceRideService extends IncyclistService {
60
66
  onData(deviceSettings: DeviceSettings, data: DeviceData): void;
61
67
  protected registerData(adapterInfo: AdapterRideInfo, data: DeviceData): void;
62
68
  protected getLastDataTS(adapterInfo: AdapterRideInfo): number;
63
- protected setLastDataTS(adapterInfo: AdapterRideInfo, ts: number): number;
69
+ protected setLastDataTS(adapterInfo: AdapterRideInfo, ts: number): void;
64
70
  protected clearLastDataTS(adapterInfo: AdapterRideInfo): void;
65
71
  private getEnabledCapabilities;
66
72
  isUpdateBusy(): boolean;
@@ -123,11 +123,14 @@ let DeviceRideService = (() => {
123
123
  return this.data;
124
124
  }
125
125
  getSelectedAdapters() {
126
- this.adapters = this.getConfiguredAdapters();
126
+ if (this.rideAdapters)
127
+ return this.rideAdapters;
128
+ this.rideAdapters = this.adapters = this.getConfiguredAdapters(true);
127
129
  if (!this.simulatorEnforced)
128
130
  return this.adapters;
129
131
  const adapter = incyclist_devices_1.AdapterFactory.create({ interface: 'simulator', name: 'Simulator' });
130
- return [{ adapter, udid: 'Simulator' + Date.now(), capabilities: adapter.getCapabilities(), isStarted: false }];
132
+ this.rideAdapters = [{ adapter, udid: 'Simulator' + Date.now(), capabilities: adapter.getCapabilities(), isStarted: false }];
133
+ return this.rideAdapters;
131
134
  }
132
135
  getAllAdapters() {
133
136
  var _a;
@@ -147,7 +150,7 @@ let DeviceRideService = (() => {
147
150
  }
148
151
  prepareEppRoute(props) {
149
152
  const { route, startPos, realityFactor, rideMode } = props;
150
- if (!route || !route.get()) {
153
+ if (!(route === null || route === void 0 ? void 0 : route.get())) {
151
154
  return null;
152
155
  }
153
156
  let res;
@@ -420,73 +423,10 @@ let DeviceRideService = (() => {
420
423
  }
421
424
  startAdapters(adapters, startType, props) {
422
425
  return __awaiter(this, void 0, void 0, function* () {
423
- const { forceErgMode, startPos, realityFactor, rideMode, route } = props || {};
426
+ var _a;
424
427
  const duplicates = this.checkAntSameDeviceID(adapters);
425
- const config = this.getDeviceConfiguration();
426
- this.startPromises = adapters === null || adapters === void 0 ? void 0 : adapters.map((ai) => __awaiter(this, void 0, void 0, function* () {
427
- var _a, _b, _c;
428
- if (duplicates.find(dai => dai.info.udid === ai.udid))
429
- return;
430
- const startProps = (0, clone_1.default)(props || {});
431
- let mode, settings, bike;
432
- if ((_a = ai.adapter) === null || _a === void 0 ? void 0 : _a.isControllable()) {
433
- bike = ai.adapter;
434
- const modeInfo = config.getModeSettings(ai.udid);
435
- mode = modeInfo === null || modeInfo === void 0 ? void 0 : modeInfo.mode;
436
- settings = (modeInfo === null || modeInfo === void 0 ? void 0 : modeInfo.settings) || {};
437
- if (!this.simulatorEnforced && forceErgMode) {
438
- const modes = bike.getSupportedCyclingModes().filter(C => C.supportsERGMode());
439
- if (modes.length > 0) {
440
- mode = new modes[0](bike);
441
- const modeInfo = config.getModeSettings(ai.udid, mode);
442
- settings = modeInfo.settings;
443
- }
444
- }
445
- if (!mode)
446
- mode = bike.getDefaultCyclingMode();
447
- bike.setCyclingMode(mode, settings);
448
- }
449
- if (startType === 'check' || startType === 'pair') {
450
- startProps.timeout = 10000;
451
- if (ai.adapter.getSettings().interface === 'ble')
452
- startProps.timeout = 30000;
453
- }
454
- if (startType === 'start') {
455
- if ((_b = ai.adapter) === null || _b === void 0 ? void 0 : _b.isControllable()) {
456
- if (bike.getCyclingMode().getModeProperty('eppSupport')) {
457
- startProps.route = this.prepareEppRoute({ route, startPos, realityFactor, rideMode });
458
- startProps.onStatusUpdate = (completed, total) => {
459
- this.emit('start-update', this.getAdapterStateInfo(ai), completed, total);
460
- };
461
- }
462
- }
463
- }
464
- const sType = ai.adapter.hasCapability(incyclist_devices_1.IncyclistCapability.Control) ? 'bike' : 'sensor';
465
- const logProps = {};
466
- logProps[sType] = ai.adapter.getUniqueName();
467
- logProps.cability = ai.adapter.getCapabilities().join('/');
468
- logProps.interface = (0, logging_1.getLegacyInterface)(ai.adapter);
469
- if (sType === 'bike') {
470
- const bike = ai.adapter;
471
- logProps.cyclingMode = (_c = bike.getCyclingMode()) === null || _c === void 0 ? void 0 : _c.getName();
472
- logProps.bikeType = bike.getCyclingMode().getSetting('bikeType');
473
- }
474
- this.logEvent(Object.assign({ message: `${startType} ${sType} request` }, logProps));
475
- return ai.adapter.start(startProps)
476
- .then((success) => __awaiter(this, void 0, void 0, function* () {
477
- if (success) {
478
- yield this.handleStartSuccess(startType, ai, duplicates, sType, logProps);
479
- return true;
480
- }
481
- yield this.handleStartFailure(startType, ai, duplicates, sType, logProps);
482
- return false;
483
- }))
484
- .catch((err) => __awaiter(this, void 0, void 0, function* () {
485
- yield this.handleStartRejection(startType, sType, logProps, err, ai, duplicates);
486
- return false;
487
- }));
488
- }));
489
- if (!this.startPromises)
428
+ this.startPromises = adapters === null || adapters === void 0 ? void 0 : adapters.map((ai) => __awaiter(this, void 0, void 0, function* () { return this.startSingleAdapter(ai, duplicates, props, startType); }));
429
+ if (!((_a = this.startPromises) === null || _a === void 0 ? void 0 : _a.length))
490
430
  return true;
491
431
  const status = yield Promise.all(this.startPromises);
492
432
  const allOK = status.find(s => s === false) === undefined;
@@ -498,6 +438,85 @@ let DeviceRideService = (() => {
498
438
  return allOK;
499
439
  });
500
440
  }
441
+ startSingleAdapter(ai, duplicates, props, startType) {
442
+ return __awaiter(this, void 0, void 0, function* () {
443
+ var _a;
444
+ if (duplicates.find(dai => dai.info.udid === ai.udid))
445
+ return;
446
+ const startProps = (0, clone_1.default)(props || {});
447
+ const { forceErgMode, startPos, realityFactor, rideMode, route } = props || {};
448
+ this.initCyclingMode(ai, forceErgMode);
449
+ if (startType === 'check' || startType === 'pair') {
450
+ this.initForPairing(startProps, ai);
451
+ }
452
+ if (startType === 'start') {
453
+ this.initForStart(ai, startProps, route, startPos, realityFactor, rideMode);
454
+ }
455
+ const sType = ai.adapter.hasCapability(incyclist_devices_1.IncyclistCapability.Control) ? 'bike' : 'sensor';
456
+ const logProps = {};
457
+ logProps[sType] = ai.adapter.getUniqueName();
458
+ logProps.cability = ai.adapter.getCapabilities().join('/');
459
+ logProps.interface = (0, logging_1.getLegacyInterface)(ai.adapter);
460
+ if (sType === 'bike') {
461
+ const bike = ai.adapter;
462
+ logProps.cyclingMode = (_a = bike.getCyclingMode()) === null || _a === void 0 ? void 0 : _a.getName();
463
+ logProps.bikeType = bike.getCyclingMode().getSetting('bikeType');
464
+ }
465
+ this.logEvent(Object.assign({ message: `${startType} ${sType} request` }, logProps));
466
+ return ai.adapter.start(startProps)
467
+ .then((success) => __awaiter(this, void 0, void 0, function* () {
468
+ if (success) {
469
+ yield this.handleStartSuccess(startType, ai, duplicates, sType, logProps);
470
+ return true;
471
+ }
472
+ yield this.handleStartFailure(startType, ai, duplicates, sType, logProps);
473
+ return false;
474
+ }))
475
+ .catch((err) => __awaiter(this, void 0, void 0, function* () {
476
+ yield this.handleStartRejection(startType, sType, logProps, err, ai, duplicates);
477
+ return false;
478
+ }));
479
+ });
480
+ }
481
+ initForStart(ai, startProps, route, startPos, realityFactor, rideMode) {
482
+ var _a;
483
+ if ((_a = ai.adapter) === null || _a === void 0 ? void 0 : _a.isControllable()) {
484
+ const bike = ai.adapter;
485
+ if (bike.getCyclingMode().getModeProperty('eppSupport')) {
486
+ startProps.route = this.prepareEppRoute({ route, startPos, realityFactor, rideMode });
487
+ startProps.onStatusUpdate = (completed, total) => {
488
+ this.emit('start-update', this.getAdapterStateInfo(ai), completed, total);
489
+ };
490
+ }
491
+ }
492
+ }
493
+ initForPairing(startProps, ai) {
494
+ startProps.timeout = 10000;
495
+ if (ai.adapter.getSettings().interface === 'ble')
496
+ startProps.timeout = 30000;
497
+ }
498
+ initCyclingMode(ai, forceErgMode) {
499
+ var _a;
500
+ let bike, mode, settings;
501
+ if ((_a = ai.adapter) === null || _a === void 0 ? void 0 : _a.isControllable()) {
502
+ bike = ai.adapter;
503
+ const config = this.getDeviceConfiguration();
504
+ const modeInfo = config.getModeSettings(ai.udid);
505
+ mode = modeInfo === null || modeInfo === void 0 ? void 0 : modeInfo.mode;
506
+ settings = (modeInfo === null || modeInfo === void 0 ? void 0 : modeInfo.settings) || {};
507
+ if (!this.simulatorEnforced && forceErgMode) {
508
+ const modes = bike.getSupportedCyclingModes().filter(C => C.supportsERGMode());
509
+ if (modes.length > 0) {
510
+ mode = new modes[0](bike);
511
+ const modeInfo = config.getModeSettings(ai.udid, mode);
512
+ settings = modeInfo.settings;
513
+ }
514
+ }
515
+ if (!mode)
516
+ mode = bike.getDefaultCyclingMode();
517
+ bike.setCyclingMode(mode, settings);
518
+ }
519
+ }
501
520
  handleStartRejection(startType, sType, logProps, err, ai, duplicates) {
502
521
  return __awaiter(this, void 0, void 0, function* () {
503
522
  this.logEvent(Object.assign(Object.assign({ message: `${startType} ${sType} request failed` }, logProps), { reason: err.message }));
@@ -582,6 +601,11 @@ let DeviceRideService = (() => {
582
601
  return;
583
602
  ai.ivToCheck = (0, timers_1.setInterval)(() => { check(); }, 1000);
584
603
  ai.isHealthy = true;
604
+ if (ai.dataStatus !== undefined && ai.dataStatus !== 'green') {
605
+ ai.dataStatus = 'green';
606
+ const { enabledCapabilities } = this.getEnabledCapabilities(ai);
607
+ this.emit('health', ai.udid, ai.dataStatus, enabledCapabilities);
608
+ }
585
609
  }
586
610
  updateOnDatahandler(ai) {
587
611
  this.logEvent({ message: 'init health check', device: ai.adapter.getName(), udid: ai.udid });
@@ -603,15 +627,20 @@ let DeviceRideService = (() => {
603
627
  }
604
628
  prepareReconnect(unhealthy) {
605
629
  return __awaiter(this, void 0, void 0, function* () {
630
+ var _a;
606
631
  this.logEvent({ message: 'prepareReconnect', device: unhealthy.adapter.getUniqueName(), udid: unhealthy.udid, noDataSince: (Date.now() - unhealthy.tsLastData), tsLastData: unhealthy.tsLastData });
632
+ this.reconnectBusy = true;
607
633
  if (unhealthy.isRestarting)
608
634
  return;
609
- yield (0, sleep_1.sleep)(UNHEALTHY_THRESHOLD - NO_DATA_THRESHOLD - 5000);
610
- if (unhealthy.isHealthy || unhealthy.isRestarting)
635
+ yield (0, sleep_1.sleep)(1000);
636
+ if (unhealthy.isHealthy || unhealthy.isRestarting) {
637
+ this.logEvent({ message: 'skipped reconnect', device: unhealthy.adapter.getUniqueName(), udid: unhealthy.udid, noDataSince: (Date.now() - unhealthy.tsLastData), tsLastData: unhealthy.tsLastData });
611
638
  return;
639
+ }
640
+ this.logEvent({ message: 'reconnect confirmed', device: unhealthy.adapter.getUniqueName(), udid: unhealthy.udid, noDataSince: (Date.now() - unhealthy.tsLastData), tsLastData: unhealthy.tsLastData });
612
641
  const ifName = unhealthy.adapter.getInterface();
613
- const adapters = this.getAllAdapters().filter(ai => ai.adapter.getInterface() === ifName);
614
- if (!adapters.find(ai => ai.isHealthy)) {
642
+ const adapters = (_a = this.rideAdapters) === null || _a === void 0 ? void 0 : _a.filter(ai => ai.adapter.getInterface() === ifName);
643
+ if (!adapters.find(ai => ai.isHealthy) && adapters.length > 1) {
615
644
  yield this.reconnectInterface(ifName, adapters);
616
645
  }
617
646
  else {
@@ -622,6 +651,10 @@ let DeviceRideService = (() => {
622
651
  }
623
652
  reconnectInterface(ifName, adapters) {
624
653
  return __awaiter(this, void 0, void 0, function* () {
654
+ if (this.reconnectBusy) {
655
+ return;
656
+ }
657
+ this.reconnectBusy = true;
625
658
  if (ifName === 'simulator')
626
659
  return;
627
660
  if (ifName === 'ble') {
@@ -631,13 +664,14 @@ let DeviceRideService = (() => {
631
664
  catch (err) {
632
665
  this.logError(err, 'reconnectInterface');
633
666
  }
667
+ this.reconnectBusy = false;
634
668
  return;
635
669
  }
636
670
  this.logger.logEvent({ message: 'restart interface', interface: ifName });
637
671
  let stopRequested = false;
638
672
  this.once('stop-ride', () => { stopRequested = true; });
639
673
  try {
640
- yield this.stopAdapters(adapters);
674
+ yield this.stopAllAdaptersOnInterface(ifName);
641
675
  if (stopRequested)
642
676
  return;
643
677
  yield this.performInterfaceReconnect(ifName);
@@ -648,20 +682,7 @@ let DeviceRideService = (() => {
648
682
  catch (err) {
649
683
  this.logger.logEvent({ message: 'restart interface failed', interface: ifName, reason: err.message });
650
684
  }
651
- this.finalizeAdaptersAfterRestart(adapters);
652
- });
653
- }
654
- finalizeAdaptersAfterRestart(adapters) {
655
- adapters.forEach(ai => {
656
- if (ai.adapter.isStarted()) {
657
- ai.tsLastData = Date.now();
658
- }
659
- else {
660
- ai.isRestarting = false;
661
- this.prepareReconnect(ai);
662
- }
663
- ai.adapter.on('data', this.deviceDataHandler);
664
- ai.isRestarting = false;
685
+ this.reconnectBusy = false;
665
686
  });
666
687
  }
667
688
  performInterfaceReconnect(ifName) {
@@ -674,11 +695,15 @@ let DeviceRideService = (() => {
674
695
  }
675
696
  });
676
697
  }
698
+ stopAllAdaptersOnInterface(ifName) {
699
+ const adapaters = this.getAllAdapters().filter(ai => ai.adapter.getInterface() === ifName);
700
+ return this.stopAdapters(adapaters);
701
+ }
677
702
  stopAdapters(adapters) {
678
703
  return __awaiter(this, void 0, void 0, function* () {
679
704
  const promisesStop = [];
680
705
  adapters.forEach(ai => {
681
- promisesStop.push(this.stopAfterRestart(ai));
706
+ promisesStop.push(this.stopDuringInterfaceRestart(ai));
682
707
  });
683
708
  if (promisesStop.length > 0) {
684
709
  yield Promise.race([(0, sleep_1.sleep)(66000), Promise.allSettled(promisesStop)]);
@@ -705,7 +730,7 @@ let DeviceRideService = (() => {
705
730
  }
706
731
  });
707
732
  }
708
- stopAfterRestart(unhealthy) {
733
+ stopDuringInterfaceRestart(unhealthy) {
709
734
  return __awaiter(this, void 0, void 0, function* () {
710
735
  this.emit('stop-adapter', unhealthy.udid);
711
736
  if (unhealthy.isRestarting) {
@@ -721,12 +746,18 @@ let DeviceRideService = (() => {
721
746
  }
722
747
  unhealthy.isRestarting = true;
723
748
  unhealthy.adapter.off('data', this.deviceDataHandler);
724
- yield unhealthy.adapter.stop();
749
+ try {
750
+ yield unhealthy.adapter.stop();
751
+ }
752
+ catch (err) {
753
+ this.logError(err, 'stopDuringInterfaceRestart');
754
+ }
725
755
  });
726
756
  }
727
757
  reconnectSingle(unhealthy) {
728
758
  return __awaiter(this, void 0, void 0, function* () {
729
759
  unhealthy.isRestarting = true;
760
+ this.stopHealthCheck(unhealthy);
730
761
  let stopRequested = false;
731
762
  this.once('stop-ride', () => { stopRequested = true; });
732
763
  this.once('stop-adapter', (udid) => {
@@ -736,7 +767,6 @@ let DeviceRideService = (() => {
736
767
  let success = false;
737
768
  do {
738
769
  this.logger.logEvent({ message: 'restart adapter', device: unhealthy.udid });
739
- unhealthy.adapter.off('data', this.deviceDataHandler);
740
770
  const adapter = unhealthy.adapter;
741
771
  try {
742
772
  const started = yield adapter.restart();
@@ -747,10 +777,13 @@ let DeviceRideService = (() => {
747
777
  }
748
778
  catch (err) {
749
779
  this.logger.logEvent({ message: 'restart adapter failed', device: unhealthy.udid, reason: err.message });
750
- this.prepareReconnect(unhealthy);
751
780
  }
752
- unhealthy.adapter.on('data', this.deviceDataHandler);
753
- if (!success) {
781
+ if (success) {
782
+ this.startHealthCheck(unhealthy);
783
+ unhealthy.isRestarting = false;
784
+ unhealthy.isStarted = true;
785
+ }
786
+ else {
754
787
  for (let i = 0; i < 60 && !stopRequested; i++)
755
788
  yield (0, sleep_1.sleep)(1000);
756
789
  }
@@ -762,6 +795,7 @@ let DeviceRideService = (() => {
762
795
  return __awaiter(this, void 0, void 0, function* () {
763
796
  yield this.lazyInit();
764
797
  const adapters = this.getSelectedAdapters();
798
+ this.rideAdapters = adapters;
765
799
  this.emit('start-request', adapters === null || adapters === void 0 ? void 0 : adapters.map(this.getAdapterStateInfo.bind(this)));
766
800
  const goodToGo = yield this.waitForPreviousStartToFinish();
767
801
  if (!goodToGo)
@@ -773,8 +807,8 @@ let DeviceRideService = (() => {
773
807
  startRetry(props) {
774
808
  return __awaiter(this, void 0, void 0, function* () {
775
809
  yield this.lazyInit();
776
- const allAdapters = this.getSelectedAdapters();
777
- this.emit('start-request', allAdapters === null || allAdapters === void 0 ? void 0 : allAdapters.map(this.getAdapterStateInfo));
810
+ const selected = this.getSelectedAdapters();
811
+ this.emit('start-request', selected === null || selected === void 0 ? void 0 : selected.map(this.getAdapterStateInfo));
778
812
  const adapters = this.getSelectedAdapters().filter(ai => !ai.adapter.isStarted());
779
813
  const goodToGo = yield this.waitForPreviousStartToFinish();
780
814
  if (!goodToGo)
@@ -802,6 +836,7 @@ let DeviceRideService = (() => {
802
836
  });
803
837
  yield Promise.allSettled(promises);
804
838
  this.startPromises = null;
839
+ delete this.rideAdapters;
805
840
  return true;
806
841
  });
807
842
  }
@@ -818,7 +853,7 @@ let DeviceRideService = (() => {
818
853
  this.logEvent({ message: 'stop devices' });
819
854
  else
820
855
  this.logEvent({ message: 'stop device', udid });
821
- const adapters = this.getSelectedAdapters();
856
+ const adapters = this.rideAdapters;
822
857
  this.emit('stop-ride');
823
858
  const promises = adapters === null || adapters === void 0 ? void 0 : adapters.filter(ai => udid ? ai.udid === udid : true).map(ai => {
824
859
  this.stopHealthCheck(ai);
@@ -827,6 +862,7 @@ let DeviceRideService = (() => {
827
862
  });
828
863
  if (promises)
829
864
  yield Promise.allSettled(promises);
865
+ delete this.rideAdapters;
830
866
  return true;
831
867
  });
832
868
  }
@@ -855,7 +891,6 @@ let DeviceRideService = (() => {
855
891
  adapters === null || adapters === void 0 ? void 0 : adapters.forEach(ai => {
856
892
  ai.tsLastData = Date.now();
857
893
  ai.adapter.resume();
858
- ai.adapter.on('data', this.deviceDataHandler);
859
894
  this.startHealthCheck(ai);
860
895
  });
861
896
  }
@@ -931,7 +966,7 @@ let DeviceRideService = (() => {
931
966
  }
932
967
  setLastDataTS(adapterInfo, ts) {
933
968
  const udid = adapterInfo.udid;
934
- return this.lastDataInfo[udid] = ts;
969
+ this.lastDataInfo[udid] = ts;
935
970
  }
936
971
  clearLastDataTS(adapterInfo) {
937
972
  const udid = adapterInfo.udid;
@@ -983,9 +1018,9 @@ let DeviceRideService = (() => {
983
1018
  const adapters = this.getSelectedAdapters();
984
1019
  this.promiseSendUpdate = [];
985
1020
  adapters === null || adapters === void 0 ? void 0 : adapters.forEach(ai => {
986
- if (ai.adapter && ai.adapter.isControllable()) {
987
- const d = ai.adapter;
988
- this.promiseSendUpdate.push(d.sendUpdate(request));
1021
+ var _a, _b, _c;
1022
+ if (((_a = ai === null || ai === void 0 ? void 0 : ai.adapter) === null || _a === void 0 ? void 0 : _a.isControllable()) && ((_b = ai === null || ai === void 0 ? void 0 : ai.adapter) === null || _b === void 0 ? void 0 : _b.isStarted()) && !((_c = ai.adapter) === null || _c === void 0 ? void 0 : _c.isStopped())) {
1023
+ this.promiseSendUpdate.push(ai.adapter.sendUpdate(request));
989
1024
  }
990
1025
  });
991
1026
  yield this.waitForUpdateFinish();
@@ -1099,10 +1134,9 @@ let DeviceRideService = (() => {
1099
1134
  if (!currentMode || currentMode.getName() !== mode) {
1100
1135
  const adapters = this.getSelectedAdapters();
1101
1136
  const adapter = (_a = adapters === null || adapters === void 0 ? void 0 : adapters.find(ai => ai.udid === udid)) === null || _a === void 0 ? void 0 : _a.adapter;
1102
- if (adapter && adapter.isControllable()) {
1103
- const device = adapter;
1104
- device.setCyclingMode(mode, settings);
1105
- yield device.sendInitCommands();
1137
+ if (adapter === null || adapter === void 0 ? void 0 : adapter.isControllable()) {
1138
+ adapter.setCyclingMode(mode, settings);
1139
+ yield adapter.sendInitCommands();
1106
1140
  }
1107
1141
  }
1108
1142
  else {
@@ -7,6 +7,7 @@ export declare class BaseCard implements Card<Route> {
7
7
  setInitialized(val: boolean): void;
8
8
  reset(): void;
9
9
  delete(): PromiseObserver<boolean>;
10
+ get id(): string;
10
11
  getId(): string;
11
12
  getTitle(): string;
12
13
  getData(): Route;
@@ -16,6 +16,9 @@ class BaseCard {
16
16
  delete() {
17
17
  throw new Error("Method not implemented.");
18
18
  }
19
+ get id() {
20
+ return this.getId();
21
+ }
19
22
  getId() {
20
23
  throw new Error("Method not implemented.");
21
24
  }
@@ -8,7 +8,7 @@ import { RouteInfo } from "../base/types";
8
8
  import { RoutesApiLoader } from "./loaders/api";
9
9
  import { MyRoutes } from "./lists/myroutes";
10
10
  import { RouteCard, SummaryCardDisplayProps } from "./cards/RouteCard";
11
- import { RouteStartSettings, SearchFilter, SearchFilterOptions } from "./types";
11
+ import { DisplayType, RouteStartSettings, SearchFilter, SearchFilterOptions } from "./types";
12
12
  import { RoutesDbLoader } from "./loaders/db";
13
13
  import { RouteListObserver } from "./RouteListObserver";
14
14
  import { ActiveImportCard } from "./cards/ActiveImportCard";
@@ -32,6 +32,8 @@ export declare class RouteListService extends IncyclistService {
32
32
  }>;
33
33
  protected previewProcessing: PromiseObserver<void>;
34
34
  protected filters: SearchFilter;
35
+ protected listTop: Record<DisplayType, number>;
36
+ protected displayType: DisplayType;
35
37
  constructor();
36
38
  setLanguage(language: string): void;
37
39
  getLanguage(): string;
@@ -49,11 +51,23 @@ export declare class RouteListService extends IncyclistService {
49
51
  routes: SummaryCardDisplayProps[];
50
52
  filters: SearchFilter;
51
53
  observer: RouteListObserver;
54
+ cards?: undefined;
55
+ } | {
56
+ routes: SummaryCardDisplayProps[];
57
+ cards: RouteCard[];
58
+ filters: SearchFilter;
59
+ observer: RouteListObserver;
52
60
  } | {
53
61
  routes: any[];
54
62
  filters: {};
55
63
  observer?: undefined;
64
+ cards?: undefined;
56
65
  };
66
+ setListTop(display: number | DisplayType, top?: number): void;
67
+ getListTop(display?: DisplayType): number;
68
+ setDisplayType(displayType: DisplayType): void;
69
+ getDisplayType(): DisplayType;
70
+ isStillLoading(): boolean;
57
71
  private applyRouteTypeFilter;
58
72
  private applyContentTypeFilter;
59
73
  private applyCountryFilter;
@@ -102,7 +116,8 @@ export declare class RouteListService extends IncyclistService {
102
116
  list: CardList<Route>;
103
117
  };
104
118
  protected resetCards(): void;
105
- protected getAllCards(): RouteCard[];
119
+ protected getAllSearchCards(): RouteCard[];
120
+ protected getUserSettings(): import("../../settings").UserSettingsService;
106
121
  }
107
122
  export declare const useRouteList: () => RouteListService;
108
123
  export declare const getRouteList: () => RouteListService;
@@ -1,4 +1,11 @@
1
1
  "use strict";
2
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
+ var useValue = arguments.length > 2;
4
+ for (var i = 0; i < initializers.length; i++) {
5
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
+ }
7
+ return useValue ? value : void 0;
8
+ };
2
9
  var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
10
  function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
11
  var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
@@ -26,13 +33,6 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
26
33
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
34
  done = true;
28
35
  };
29
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
- var useValue = arguments.length > 2;
31
- for (var i = 0; i < initializers.length; i++) {
32
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
- }
34
- return useValue ? value : void 0;
35
- };
36
36
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
37
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
38
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -72,15 +72,21 @@ const selected_1 = require("./lists/selected");
72
72
  const alternatives_1 = require("./lists/alternatives");
73
73
  const utils_1 = require("./utils");
74
74
  const utils_2 = require("incyclist-devices/lib/utils/utils");
75
+ const settings_1 = require("../../settings");
76
+ const decorators_1 = require("../../base/decorators");
75
77
  let RouteListService = (() => {
76
78
  let _classDecorators = [types_1.Singleton];
77
79
  let _classDescriptor;
78
80
  let _classExtraInitializers = [];
79
81
  let _classThis;
80
82
  let _classSuper = service_1.IncyclistService;
83
+ let _instanceExtraInitializers = [];
84
+ let _getUserSettings_decorators;
81
85
  var RouteListService = _classThis = class extends _classSuper {
82
86
  constructor() {
83
87
  super('RouteList');
88
+ this.myRoutes = __runInitializers(this, _instanceExtraInitializers);
89
+ this.listTop = { list: undefined, tiles: undefined };
84
90
  this.myRoutes = new myroutes_1.MyRoutes('myRoutes', 'My Routes');
85
91
  this.selectedRoutes = new selected_1.SelectedRoutes('selected', 'Selected For Me');
86
92
  this.alternatives = new alternatives_1.AlternativeRoutes('alternaties', 'Alternatives');
@@ -130,8 +136,9 @@ let RouteListService = (() => {
130
136
  this.observer = new RouteListObserver_1.RouteListObserver(this);
131
137
  emitStartEvent();
132
138
  }
133
- if (!this.initialized && !this.preloadObserver) {
134
- this.preload();
139
+ if (this.isStillLoading()) {
140
+ const preload = this.preload();
141
+ preload.start().then(emitLoadedEvent);
135
142
  }
136
143
  else if (this.initialized) {
137
144
  this.unselect();
@@ -165,7 +172,7 @@ let RouteListService = (() => {
165
172
  if (requestedFilters)
166
173
  this.filters = requestedFilters;
167
174
  const filters = requestedFilters || this.filters;
168
- let routes = Array.from(this.getAllCards().map(c => c.getDisplayProperties()));
175
+ let routes = Array.from(this.getAllSearchCards().map(c => c.getDisplayProperties()));
169
176
  routes.sort((a, b) => a.title > b.title ? 1 : -1);
170
177
  if (!filters) {
171
178
  return { routes, filters, observer: this.observer };
@@ -177,13 +184,33 @@ let RouteListService = (() => {
177
184
  routes = this.applyCountryFilter(filters, routes);
178
185
  routes = this.applyContentTypeFilter(filters, routes);
179
186
  routes = this.applyRouteTypeFilter(filters, routes);
180
- return { routes, filters, observer: this.observer };
187
+ const cards = routes.map(r => this.getCard(r.id));
188
+ return { routes, cards, filters, observer: this.observer };
181
189
  }
182
190
  catch (err) {
183
191
  this.logError(err, 'search');
184
192
  return { routes: [], filters: {} };
185
193
  }
186
194
  }
195
+ setListTop(display, top) {
196
+ const displayType = typeof display === 'number' ? this.displayType : display;
197
+ const topValue = typeof display === 'number' ? display : top;
198
+ this.listTop[displayType] = topValue;
199
+ }
200
+ getListTop(display = this.displayType) {
201
+ return this.listTop[display];
202
+ }
203
+ setDisplayType(displayType) {
204
+ this.displayType = displayType;
205
+ this.getUserSettings().set('preferences.routeListDisplayType', displayType);
206
+ }
207
+ getDisplayType() {
208
+ var _a;
209
+ return (_a = this.displayType) !== null && _a !== void 0 ? _a : this.getUserSettings().get('preferences.routeListDisplayType', 'list');
210
+ }
211
+ isStillLoading() {
212
+ return !this.initialized && !this.preloadObserver;
213
+ }
187
214
  applyRouteTypeFilter(filters, routes) {
188
215
  if (filters.routeType) {
189
216
  const loop = filters.routeType === undefined || filters.routeType === 'Loop';
@@ -264,9 +291,13 @@ let RouteListService = (() => {
264
291
  try {
265
292
  list.getCards().forEach((card, idx) => {
266
293
  card.setInitialized(true);
267
- if (idx < item + itemsInSlide + 2) {
268
- card.setVisible(true);
269
- }
294
+ });
295
+ process.nextTick(() => {
296
+ list.getCards().forEach((card, idx) => {
297
+ if (idx < item + itemsInSlide) {
298
+ card.setVisible(true);
299
+ }
300
+ });
270
301
  });
271
302
  setTimeout(() => { this.onCarouselUpdated(list, item, itemsInSlide); }, 1000);
272
303
  }
@@ -751,23 +782,28 @@ let RouteListService = (() => {
751
782
  });
752
783
  });
753
784
  }
754
- getAllCards() {
785
+ getAllSearchCards() {
755
786
  var _a;
756
787
  const cards = [];
757
788
  (_a = this.getLists(false)) === null || _a === void 0 ? void 0 : _a.forEach(list => {
758
789
  list.getCards().forEach((card) => {
759
- if (card.getCardType() === 'Import' || card.getCardType() === 'Free-Ride')
790
+ if (card.getCardType() === 'Import' || card.getCardType() === 'Free-Ride' || card.getCardType() === 'ActiveImport')
760
791
  return;
761
792
  cards.push(card);
762
793
  });
763
794
  });
764
795
  return cards;
765
796
  }
797
+ getUserSettings() {
798
+ return (0, settings_1.useUserSettings)();
799
+ }
766
800
  };
767
801
  __setFunctionName(_classThis, "RouteListService");
768
802
  (() => {
769
803
  var _a;
770
804
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
805
+ _getUserSettings_decorators = [decorators_1.Injectable];
806
+ __esDecorate(_classThis, null, _getUserSettings_decorators, { kind: "method", name: "getUserSettings", static: false, private: false, access: { has: obj => "getUserSettings" in obj, get: obj => obj.getUserSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
771
807
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
772
808
  RouteListService = _classThis = _classDescriptor.value;
773
809
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -3,6 +3,7 @@ import { RouteCardType } from "./cards/types";
3
3
  export interface RouteStartSettings {
4
4
  type: RouteCardType;
5
5
  }
6
+ export type DisplayType = 'list' | 'tiles';
6
7
  export interface FreeRidePoints extends LatLng {
7
8
  distance?: number;
8
9
  tag?: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.26"
6
6
  },
@@ -10,14 +10,14 @@
10
10
  "@typescript-eslint/eslint-plugin": "^8.19.0",
11
11
  "@typescript-eslint/parser": "^8.18.2",
12
12
  "dotenv": "^16.4.7",
13
- "eslint": "^9.15.0",
13
+ "eslint": "^9.18.0",
14
14
  "formdata-node": "^6.0.3",
15
15
  "jest": "^29.7.0",
16
16
  "jsdoc": "^4.0.4",
17
17
  "ts-jest": "^29.2.5",
18
18
  "typedoc": "^0.27.4",
19
19
  "typedoc-plugin-markdown": "^4.3.2",
20
- "typedoc-plugin-no-inherit": "^1.4.0",
20
+ "typedoc-plugin-no-inherit": "^1.5.0",
21
21
  "typescript": "^5.7.2"
22
22
  },
23
23
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "axios": "^1.7.7",
45
- "incyclist-devices": "^2.3.7",
45
+ "incyclist-devices": "^2.3.8",
46
46
  "promise.any": "^2.0.6",
47
47
  "semver": "^7.6.3",
48
48
  "tcx-builder": "^1.1.1",