incyclist-devices 1.4.8 → 1.4.11
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/CyclingMode.js +0 -1
- package/lib/Device.d.ts +2 -0
- package/lib/Device.js +2 -0
- package/lib/DeviceProtocol.d.ts +4 -1
- package/lib/DeviceProtocol.js +0 -1
- package/lib/DeviceSupport.d.ts +2 -0
- package/lib/DeviceSupport.js +11 -22
- package/lib/ant/AntAdapter.js +0 -1
- package/lib/ant/AntScanner.js +7 -20
- package/lib/ant/antfe/AntFEAdapter.js +8 -8
- package/lib/ant/anthrm/AntHrmAdapter.js +1 -1
- package/lib/ant/utils.js +1 -3
- package/lib/calculations.js +0 -1
- package/lib/daum/DaumAdapter.js +18 -13
- package/lib/daum/ERGCyclingMode.js +2 -1
- package/lib/daum/SmartTrainerCyclingMode.js +0 -1
- package/lib/daum/classic/DaumClassicAdapter.js +1 -1
- package/lib/daum/classic/DaumClassicProtocol.js +7 -19
- package/lib/daum/classic/bike.js +26 -26
- package/lib/daum/classic/utils.js +0 -1
- package/lib/daum/constants.js +0 -1
- package/lib/daum/premium/DaumPremiumAdapter.js +1 -1
- package/lib/daum/premium/DaumPremiumProtocol.js +7 -19
- package/lib/daum/premium/bike.js +21 -19
- package/lib/daum/premium/utils.js +0 -1
- package/lib/kettler/comms.d.ts +48 -0
- package/lib/kettler/comms.js +194 -0
- package/lib/kettler/ergo-racer/adapter.d.ts +99 -0
- package/lib/kettler/ergo-racer/adapter.js +587 -0
- package/lib/kettler/ergo-racer/modes/power-meter.d.ts +18 -0
- package/lib/kettler/ergo-racer/modes/power-meter.js +78 -0
- package/lib/kettler/ergo-racer/protocol.d.ts +41 -0
- package/lib/kettler/ergo-racer/protocol.js +191 -0
- package/lib/simulator/Simulator.js +2 -15
- package/lib/types/command.d.ts +8 -0
- package/lib/types/command.js +2 -0
- package/lib/types/route.js +0 -1
- package/lib/types/user.js +0 -1
- package/lib/utils.js +1 -3
- package/package.json +1 -1
|
@@ -1,23 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
22
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -27,6 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
27
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
28
9
|
});
|
|
29
10
|
};
|
|
11
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
12
|
+
if (mod && mod.__esModule) return mod;
|
|
13
|
+
var result = {};
|
|
14
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
15
|
+
result["default"] = mod;
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
30
18
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
19
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
20
|
};
|
package/lib/daum/premium/bike.js
CHANGED
|
@@ -12,7 +12,6 @@ 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
|
-
exports.Daum8iSerial = exports.Daum8iTcp = void 0;
|
|
16
15
|
const constants_1 = require("../constants");
|
|
17
16
|
const tcpserial_1 = __importDefault(require("./tcpserial"));
|
|
18
17
|
const utils_1 = require("./utils");
|
|
@@ -26,6 +25,8 @@ const OPEN_TIMEOUT = 1000;
|
|
|
26
25
|
const DAUM_PREMIUM_DEFAULT_PORT = 51955;
|
|
27
26
|
const DAUM_PREMIUM_DEFAULT_HOST = '127.0.0.1';
|
|
28
27
|
const MAX_DATA_BLOCK_SIZE = 512;
|
|
28
|
+
const DS_BITS_OFF = 0;
|
|
29
|
+
const DS_BITS_ENDLESS_RACE = 2;
|
|
29
30
|
var __SerialPort = undefined;
|
|
30
31
|
var net = undefined;
|
|
31
32
|
const DEBUG_LOGGER = {
|
|
@@ -406,7 +407,7 @@ class Daum8i {
|
|
|
406
407
|
incoming = bufferData;
|
|
407
408
|
}
|
|
408
409
|
const response = [...incoming];
|
|
409
|
-
this.logger.logEvent({ message: 'sendCommand:RECV', data:
|
|
410
|
+
this.logger.logEvent({ message: 'sendCommand:RECV', data: utils_1.hexstr(response) });
|
|
410
411
|
for (let i = 0; i < incoming.length; i++) {
|
|
411
412
|
const getRemaining = () => {
|
|
412
413
|
let remaining = '';
|
|
@@ -439,11 +440,11 @@ class Daum8i {
|
|
|
439
440
|
}
|
|
440
441
|
else if (c === 0x17) {
|
|
441
442
|
const remaining = getRemaining();
|
|
442
|
-
this.logger.logEvent({ message: "sendCommand:received:", duration: Date.now() - this.state.sending.tsRequest, port: portName, cmd: `${cmd} [${
|
|
443
|
+
this.logger.logEvent({ message: "sendCommand:received:", duration: Date.now() - this.state.sending.tsRequest, port: portName, cmd: `${cmd} [${utils_1.hexstr(cmd)}]`, remaining: utils_1.hexstr(remaining) });
|
|
443
444
|
this.state.waitingForEnd = false;
|
|
444
445
|
const cmdStr = cmd.substring(0, cmd.length - 2);
|
|
445
446
|
const checksumExtracted = cmd.slice(-2);
|
|
446
|
-
const checksumCalculated =
|
|
447
|
+
const checksumCalculated = utils_1.checkSum(utils_1.getAsciiArrayFromStr(cmdStr), []);
|
|
447
448
|
if (checksumExtracted === checksumCalculated) {
|
|
448
449
|
this.sendACK();
|
|
449
450
|
if (this.state.sending && this.state.sending.responseCheckIv) {
|
|
@@ -485,8 +486,8 @@ class Daum8i {
|
|
|
485
486
|
this.state.busy = true;
|
|
486
487
|
}
|
|
487
488
|
else {
|
|
488
|
-
const message =
|
|
489
|
-
this.logger.logEvent({ message: 'sendCommand:waiting', port: this.portName, cmd: command, hex:
|
|
489
|
+
const message = utils_1.buildMessage(command, payload);
|
|
490
|
+
this.logger.logEvent({ message: 'sendCommand:waiting', port: this.portName, cmd: command, hex: utils_1.hexstr(message) });
|
|
490
491
|
const busyWait = () => {
|
|
491
492
|
return new Promise((done) => {
|
|
492
493
|
let start = Date.now();
|
|
@@ -507,7 +508,7 @@ class Daum8i {
|
|
|
507
508
|
};
|
|
508
509
|
const res = yield busyWait();
|
|
509
510
|
if (!res) {
|
|
510
|
-
this.logger.logEvent({ message: 'sendCommand:busy timeout', port: this.portName, cmd: command, hex:
|
|
511
|
+
this.logger.logEvent({ message: 'sendCommand:busy timeout', port: this.portName, cmd: command, hex: utils_1.hexstr(message), duration: Date.now() - tsRequest });
|
|
511
512
|
return reject(new Error('BUSY timeout'));
|
|
512
513
|
}
|
|
513
514
|
this.state.busy = true;
|
|
@@ -525,10 +526,10 @@ class Daum8i {
|
|
|
525
526
|
const portName = this.portName;
|
|
526
527
|
this.state.received = [];
|
|
527
528
|
try {
|
|
528
|
-
const message =
|
|
529
|
+
const message = utils_1.buildMessage(command, payload);
|
|
529
530
|
const start = Date.now();
|
|
530
531
|
const timeout = start + this.getTimeoutValue();
|
|
531
|
-
this.logger.logEvent({ message: "sendCommand:sending:", port: this.portName, cmd: command, hex:
|
|
532
|
+
this.logger.logEvent({ message: "sendCommand:sending:", port: this.portName, cmd: command, hex: utils_1.hexstr(message) });
|
|
532
533
|
this.state.writeBusy = true;
|
|
533
534
|
if (!this.connected || port === undefined) {
|
|
534
535
|
this.logger.logEvent({ message: "sendCommand:error: not connected", port: this.portName });
|
|
@@ -588,10 +589,10 @@ class Daum8i {
|
|
|
588
589
|
buffer.writeUInt16LE(0, 2);
|
|
589
590
|
}
|
|
590
591
|
const cmdData = Uint8Array.from(buffer);
|
|
591
|
-
return this.sendDaum8iCommand('M70', cmdType,
|
|
592
|
+
return this.sendDaum8iCommand('M70', cmdType, utils_1.bin2esc(cmdData))
|
|
592
593
|
.then((res) => {
|
|
593
594
|
const resData = Uint8Array.from(res, x => x.charCodeAt(0));
|
|
594
|
-
const cmd =
|
|
595
|
+
const cmd = utils_1.esc2bin(resData);
|
|
595
596
|
return cmd;
|
|
596
597
|
});
|
|
597
598
|
}
|
|
@@ -616,7 +617,7 @@ class Daum8i {
|
|
|
616
617
|
else if (str === '7')
|
|
617
618
|
deviceType = 'lyps';
|
|
618
619
|
else
|
|
619
|
-
throw (new Error(`unknown device type ${typeof str === 'string' ?
|
|
620
|
+
throw (new Error(`unknown device type ${typeof str === 'string' ? utils_1.ascii(str.charAt(0)) : str}`));
|
|
620
621
|
return deviceType;
|
|
621
622
|
});
|
|
622
623
|
}
|
|
@@ -631,7 +632,7 @@ class Daum8i {
|
|
|
631
632
|
else if (str === '2')
|
|
632
633
|
deviceType = constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
|
|
633
634
|
else {
|
|
634
|
-
throw (new Error(`unknown actual device type ${typeof str === 'string' ?
|
|
635
|
+
throw (new Error(`unknown actual device type ${typeof str === 'string' ? utils_1.ascii(str.charAt(0)) : str}`));
|
|
635
636
|
}
|
|
636
637
|
this.state.actualBikeType = deviceType;
|
|
637
638
|
return deviceType;
|
|
@@ -673,12 +674,12 @@ class Daum8i {
|
|
|
673
674
|
getTrainingData() {
|
|
674
675
|
return this.sendDaum8iCommand('X70', 'AF', [])
|
|
675
676
|
.then((data) => {
|
|
676
|
-
const td =
|
|
677
|
+
const td = utils_1.parseTrainingData(data);
|
|
677
678
|
return td;
|
|
678
679
|
});
|
|
679
680
|
}
|
|
680
681
|
setLoadControl(enabled) {
|
|
681
|
-
const val = enabled ?
|
|
682
|
+
const val = enabled ? utils_1.ascii('1') : utils_1.ascii('0');
|
|
682
683
|
return this.sendDaum8iCommand('S20', 'BF', [val])
|
|
683
684
|
.then((data) => {
|
|
684
685
|
const res = data === '1';
|
|
@@ -712,7 +713,7 @@ class Daum8i {
|
|
|
712
713
|
setPerson(person) {
|
|
713
714
|
const { sex, age, length, weight } = person;
|
|
714
715
|
this.logger.logEvent({ message: 'setPerson() request', sex, age, length, weight });
|
|
715
|
-
return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PERSON_SET, 'BF',
|
|
716
|
+
return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PERSON_SET, 'BF', utils_1.getPersonData(person))
|
|
716
717
|
.then((res) => {
|
|
717
718
|
const buffer = Buffer.from(res);
|
|
718
719
|
const success = buffer.readInt16LE(0) === utils_1.ReservedCommands.PERSON_SET;
|
|
@@ -736,9 +737,10 @@ class Daum8i {
|
|
|
736
737
|
}
|
|
737
738
|
programUploadStart(bikeType, route) {
|
|
738
739
|
const payload = Buffer.alloc(40);
|
|
739
|
-
const epp = route ?
|
|
740
|
+
const epp = route ? utils_1.routeToEpp(route) : undefined;
|
|
740
741
|
const eppLength = epp ? epp.length : 0;
|
|
741
|
-
const bikeTypeVal =
|
|
742
|
+
const bikeTypeVal = utils_1.getBikeType(bikeType);
|
|
743
|
+
const wBits = route.lapMode ? DS_BITS_ENDLESS_RACE : DS_BITS_OFF;
|
|
742
744
|
payload.writeInt32LE(0, 0);
|
|
743
745
|
payload.writeInt8(bikeTypeVal, 4);
|
|
744
746
|
payload.writeInt8(0, 5);
|
|
@@ -750,7 +752,7 @@ class Daum8i {
|
|
|
750
752
|
payload.writeInt16LE(0, 24);
|
|
751
753
|
payload.writeInt16LE(0, 26);
|
|
752
754
|
payload.writeInt16LE(0, 28);
|
|
753
|
-
payload.writeInt16LE(
|
|
755
|
+
payload.writeInt16LE(wBits, 30);
|
|
754
756
|
payload.writeInt32LE(7, 32);
|
|
755
757
|
payload.writeInt32LE(eppLength, 36);
|
|
756
758
|
this.logger.logEvent({ message: 'programUploadStart() request', bikeType, length: eppLength });
|
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getPersonData = exports.parseTrainingData = exports.routeToEpp = exports.getBikeType = exports.BikeType = exports.ReservedCommands = exports.Int32ToIntArray = exports.Int16ToIntArray = exports.Float32ToIntArray = exports.Float32ToHex = exports.getAsciiArrayFromStr = exports.asciiArrayToString = exports.charArrayToString = exports.ascii = exports.append = exports.getHex = exports.hexstr = exports.getMessageData = exports.buildMessage = exports.checkSum = exports.esc2bin = exports.bin2esc = void 0;
|
|
7
6
|
const win32filetime_1 = __importDefault(require("win32filetime"));
|
|
8
7
|
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
|
|
9
8
|
function bin2esc(arr) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { DeviceProtocol } from "../DeviceProtocol";
|
|
3
|
+
import { EventLogger } from "gd-eventlog";
|
|
4
|
+
import { Command } from "../types/command";
|
|
5
|
+
import EventEmitter from "events";
|
|
6
|
+
export declare type SerialCommsProps = {
|
|
7
|
+
logger?: EventLogger;
|
|
8
|
+
protocol: DeviceProtocol;
|
|
9
|
+
port: string;
|
|
10
|
+
settings?: any;
|
|
11
|
+
};
|
|
12
|
+
declare enum SerialCommsState {
|
|
13
|
+
Idle = 0,
|
|
14
|
+
Connecting = 1,
|
|
15
|
+
Connected = 2,
|
|
16
|
+
Disconnecting = 3,
|
|
17
|
+
Disconnected = 4,
|
|
18
|
+
Error = 5
|
|
19
|
+
}
|
|
20
|
+
export default class KettlerSerialComms<T extends Command> extends EventEmitter {
|
|
21
|
+
private logger;
|
|
22
|
+
private port;
|
|
23
|
+
private sp;
|
|
24
|
+
private queue;
|
|
25
|
+
private state;
|
|
26
|
+
private settings;
|
|
27
|
+
private worker;
|
|
28
|
+
private sendState;
|
|
29
|
+
private currentCmd;
|
|
30
|
+
private protocol;
|
|
31
|
+
constructor(opts: SerialCommsProps);
|
|
32
|
+
getPort(): string;
|
|
33
|
+
setPort(port: any): void;
|
|
34
|
+
isConnected(): boolean;
|
|
35
|
+
stateIn: (allowedStates: SerialCommsState[]) => boolean;
|
|
36
|
+
onPortOpen(): void;
|
|
37
|
+
onPortClose(): Promise<void>;
|
|
38
|
+
onPortError(err: any): void;
|
|
39
|
+
open(): void;
|
|
40
|
+
close(): void;
|
|
41
|
+
startWorker(): void;
|
|
42
|
+
stopWorker(): void;
|
|
43
|
+
onData(data: string | Buffer): void;
|
|
44
|
+
write(cmd: Command): void;
|
|
45
|
+
sendNextCommand(): Command | undefined;
|
|
46
|
+
send(cmd: Command): void;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
16
|
+
const utils_1 = require("../utils");
|
|
17
|
+
const events_1 = __importDefault(require("events"));
|
|
18
|
+
const DEFAULT_RCV_TIMEOUT = 500;
|
|
19
|
+
const DEBUG_LOGGER = {
|
|
20
|
+
log: (e, ...args) => console.log(e, ...args),
|
|
21
|
+
logEvent: (event) => console.log(JSON.stringify(event))
|
|
22
|
+
};
|
|
23
|
+
var SerialCommsState;
|
|
24
|
+
(function (SerialCommsState) {
|
|
25
|
+
SerialCommsState[SerialCommsState["Idle"] = 0] = "Idle";
|
|
26
|
+
SerialCommsState[SerialCommsState["Connecting"] = 1] = "Connecting";
|
|
27
|
+
SerialCommsState[SerialCommsState["Connected"] = 2] = "Connected";
|
|
28
|
+
SerialCommsState[SerialCommsState["Disconnecting"] = 3] = "Disconnecting";
|
|
29
|
+
SerialCommsState[SerialCommsState["Disconnected"] = 4] = "Disconnected";
|
|
30
|
+
SerialCommsState[SerialCommsState["Error"] = 5] = "Error";
|
|
31
|
+
})(SerialCommsState || (SerialCommsState = {}));
|
|
32
|
+
var SendState;
|
|
33
|
+
(function (SendState) {
|
|
34
|
+
SendState[SendState["Idle"] = 0] = "Idle";
|
|
35
|
+
SendState[SendState["Sending"] = 1] = "Sending";
|
|
36
|
+
SendState[SendState["Receiving"] = 2] = "Receiving";
|
|
37
|
+
})(SendState || (SendState = {}));
|
|
38
|
+
const CRLF = '\r\n';
|
|
39
|
+
class KettlerSerialComms extends events_1.default {
|
|
40
|
+
constructor(opts) {
|
|
41
|
+
super();
|
|
42
|
+
this.stateIn = (allowedStates) => {
|
|
43
|
+
return allowedStates.indexOf(this.state) >= 0;
|
|
44
|
+
};
|
|
45
|
+
this.logger = process.env.DEBUG ? DEBUG_LOGGER : (opts.logger || new gd_eventlog_1.EventLogger(opts.protocol.getName()));
|
|
46
|
+
this.port = opts.port || process.env.COM_PORT;
|
|
47
|
+
this.sp = undefined;
|
|
48
|
+
this.queue = new utils_1.Queue();
|
|
49
|
+
this.state = SerialCommsState.Idle;
|
|
50
|
+
this.sendState = SendState.Idle;
|
|
51
|
+
this.settings = opts.settings || {};
|
|
52
|
+
this.currentCmd = undefined;
|
|
53
|
+
this.protocol = opts.protocol;
|
|
54
|
+
}
|
|
55
|
+
getPort() {
|
|
56
|
+
return this.port;
|
|
57
|
+
}
|
|
58
|
+
setPort(port) {
|
|
59
|
+
this.port = port;
|
|
60
|
+
}
|
|
61
|
+
isConnected() {
|
|
62
|
+
return this.state === SerialCommsState.Connected;
|
|
63
|
+
}
|
|
64
|
+
onPortOpen() {
|
|
65
|
+
this.logger.logEvent({ message: 'port opened', port: this.getPort() });
|
|
66
|
+
this.state = SerialCommsState.Connected;
|
|
67
|
+
this.sendState = SendState.Idle;
|
|
68
|
+
this.startWorker();
|
|
69
|
+
this.emit('opened');
|
|
70
|
+
}
|
|
71
|
+
onPortClose() {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
this.logger.logEvent({ message: 'port closed', port: this.getPort() });
|
|
74
|
+
this.stopWorker();
|
|
75
|
+
if (this.sendState === SendState.Sending) {
|
|
76
|
+
}
|
|
77
|
+
this.state = SerialCommsState.Disconnected;
|
|
78
|
+
this.sendState = SendState.Idle;
|
|
79
|
+
this.queue.clear();
|
|
80
|
+
this.emit('closed');
|
|
81
|
+
this.sp.removeAllListeners();
|
|
82
|
+
this.sp = undefined;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
onPortError(err) {
|
|
86
|
+
if (this.stateIn([SerialCommsState.Connected, SerialCommsState.Disconnected]))
|
|
87
|
+
return;
|
|
88
|
+
if (this.state === SerialCommsState.Disconnecting && (err.message === 'Port is not open' || err.message === 'Writing to COM port (GetOverlappedResult): Operation aborted'))
|
|
89
|
+
return;
|
|
90
|
+
if (this.state === SerialCommsState.Connecting && (err.message === 'Port is already open' || err.message === 'Port is opening'))
|
|
91
|
+
return;
|
|
92
|
+
this.logger.logEvent({ message: "port error:", port: this.getPort(), error: err.message, stack: err.stack, state: this.state });
|
|
93
|
+
this.emit('error', err);
|
|
94
|
+
}
|
|
95
|
+
open() {
|
|
96
|
+
this.logger.logEvent({ message: "open()", port: this.getPort() });
|
|
97
|
+
if (this.stateIn([SerialCommsState.Connected, SerialCommsState.Connecting, SerialCommsState.Disconnecting])) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const SerialPort = this.protocol.getSerialPort();
|
|
102
|
+
if (this.sp === undefined) {
|
|
103
|
+
this.sp = new SerialPort(this.getPort(), this.settings);
|
|
104
|
+
this.sp.on('open', () => { this.onPortOpen(); });
|
|
105
|
+
this.sp.on('close', () => { this.onPortClose(); });
|
|
106
|
+
this.sp.on('error', (error) => { this.onPortError(error); });
|
|
107
|
+
}
|
|
108
|
+
this.state = SerialCommsState.Connecting;
|
|
109
|
+
const parser = this.sp.pipe(new SerialPort.parsers.Readline({ delimiter: CRLF }));
|
|
110
|
+
parser.on('data', (data) => { this.onData(data); });
|
|
111
|
+
this.sp.open();
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.logger.logEvent({ message: "error", fn: 'open()', error: err.message });
|
|
115
|
+
this.state = SerialCommsState.Disconnected;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
close() {
|
|
119
|
+
this.logger.logEvent({ message: 'close()', port: this.getPort() });
|
|
120
|
+
if (this.stateIn([SerialCommsState.Idle, SerialCommsState.Disconnected, SerialCommsState.Disconnecting])) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.state = SerialCommsState.Disconnecting;
|
|
124
|
+
this.sp.close();
|
|
125
|
+
}
|
|
126
|
+
startWorker() {
|
|
127
|
+
this.worker = setInterval(() => {
|
|
128
|
+
this.sendNextCommand();
|
|
129
|
+
}, 50);
|
|
130
|
+
}
|
|
131
|
+
stopWorker() {
|
|
132
|
+
if (this.worker) {
|
|
133
|
+
clearInterval(this.worker);
|
|
134
|
+
this.worker = undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
onData(data) {
|
|
138
|
+
this.sendState = SendState.Idle;
|
|
139
|
+
this.currentCmd = undefined;
|
|
140
|
+
this.logger.logEvent({ message: "sendCommand:receiving:", data: data });
|
|
141
|
+
if (typeof data === 'string') {
|
|
142
|
+
if (data.length > 2)
|
|
143
|
+
data = data.trim();
|
|
144
|
+
this.currentCmd.onResponse(data);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.currentCmd.onResponse(data);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
write(cmd) {
|
|
151
|
+
this.sendState = SendState.Sending;
|
|
152
|
+
const { logStr, message, timeout = (this.settings.timeout || DEFAULT_RCV_TIMEOUT) } = cmd;
|
|
153
|
+
const msg = typeof message === 'string' ? message : utils_1.hexstr(message);
|
|
154
|
+
const onError = (err) => {
|
|
155
|
+
this.logger.logEvent({ message: "sendCommand:error:", cmd: logStr, error: err.message, port: this.getPort() });
|
|
156
|
+
cmd.onError(err);
|
|
157
|
+
this.sendState = SendState.Idle;
|
|
158
|
+
this.currentCmd = undefined;
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
this.logger.logEvent({ message: "sendCommand:sending:", cmd: logStr, msg, port: this.getPort() });
|
|
162
|
+
if (typeof (message) !== 'string')
|
|
163
|
+
throw new Error('message must be a string');
|
|
164
|
+
this.sp.write(msg + CRLF, (err) => {
|
|
165
|
+
this.sendState = SendState.Receiving;
|
|
166
|
+
this.currentCmd = cmd;
|
|
167
|
+
if (timeout) {
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
if (this.sendState === SendState.Receiving) {
|
|
170
|
+
onError(new Error("timeout"));
|
|
171
|
+
}
|
|
172
|
+
}, timeout);
|
|
173
|
+
}
|
|
174
|
+
if (err)
|
|
175
|
+
onError(err);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
onError(err);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
sendNextCommand() {
|
|
183
|
+
if (this.sendState !== SendState.Idle) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const cmd = this.queue.dequeue();
|
|
187
|
+
if (cmd)
|
|
188
|
+
this.write(cmd);
|
|
189
|
+
}
|
|
190
|
+
send(cmd) {
|
|
191
|
+
this.queue.enqueue(cmd);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.default = KettlerSerialComms;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { DeviceSettings } from "../../DeviceProtocol";
|
|
2
|
+
import DeviceAdapterBase, { DeviceAdapter, DeviceData, Bike } from "../../Device";
|
|
3
|
+
import { DeviceProtocol } from "../../DeviceProtocol";
|
|
4
|
+
import { EventLogger } from "gd-eventlog";
|
|
5
|
+
import { Command } from "../../types/command";
|
|
6
|
+
import CyclingMode, { IncyclistBikeData } from "../../CyclingMode";
|
|
7
|
+
import { User } from "../../types/user";
|
|
8
|
+
export interface KettlerRacerCommand extends Command {
|
|
9
|
+
}
|
|
10
|
+
export interface KettlerExtendedBikeData {
|
|
11
|
+
}
|
|
12
|
+
export interface KettlerBikeData {
|
|
13
|
+
heartrate?: number;
|
|
14
|
+
cadence?: number;
|
|
15
|
+
speed?: number;
|
|
16
|
+
distance?: number;
|
|
17
|
+
requestedPower?: number;
|
|
18
|
+
energy?: number;
|
|
19
|
+
timestamp?: number;
|
|
20
|
+
time: number;
|
|
21
|
+
power: number;
|
|
22
|
+
}
|
|
23
|
+
export interface KettlerDeviceSettings extends DeviceSettings {
|
|
24
|
+
userSettings?: User;
|
|
25
|
+
bikeSettings?: any;
|
|
26
|
+
cyclingMode?: CyclingMode;
|
|
27
|
+
}
|
|
28
|
+
export default class KettlerRacerAdapter extends DeviceAdapterBase implements DeviceAdapter, Bike {
|
|
29
|
+
private id;
|
|
30
|
+
private settings;
|
|
31
|
+
private ignoreHrm;
|
|
32
|
+
private ignoreBike;
|
|
33
|
+
private ignorePower;
|
|
34
|
+
private logger;
|
|
35
|
+
private paused;
|
|
36
|
+
private comms;
|
|
37
|
+
private iv;
|
|
38
|
+
private requests;
|
|
39
|
+
private data;
|
|
40
|
+
private idata;
|
|
41
|
+
private kettlerData;
|
|
42
|
+
private updateBusy;
|
|
43
|
+
private requestBusy;
|
|
44
|
+
constructor(protocol: DeviceProtocol, settings: DeviceSettings);
|
|
45
|
+
isBike(): boolean;
|
|
46
|
+
isPower(): boolean;
|
|
47
|
+
isHrm(): boolean;
|
|
48
|
+
setID(id: any): void;
|
|
49
|
+
getID(): string;
|
|
50
|
+
getName(): string;
|
|
51
|
+
getPort(): string;
|
|
52
|
+
setIgnoreHrm(ignore: boolean): void;
|
|
53
|
+
setIgnorePower(ignore: boolean): void;
|
|
54
|
+
setIgnoreBike(ignore: boolean): void;
|
|
55
|
+
getLogger(): EventLogger;
|
|
56
|
+
getUserSettings(): User;
|
|
57
|
+
getWeight(): number;
|
|
58
|
+
setComputerMode(): Promise<boolean>;
|
|
59
|
+
setClientMode(): Promise<boolean>;
|
|
60
|
+
reset(): Promise<boolean>;
|
|
61
|
+
getIdentifier(): Promise<string>;
|
|
62
|
+
getInterface(): Promise<string>;
|
|
63
|
+
getVersion(): Promise<string>;
|
|
64
|
+
getCalibration(): Promise<string>;
|
|
65
|
+
startTraining(): Promise<string>;
|
|
66
|
+
unknownSN(): Promise<string>;
|
|
67
|
+
setBaudrate(baudrate: number): Promise<string>;
|
|
68
|
+
setPower(power: number): Promise<string>;
|
|
69
|
+
getExtendedStatus(): Promise<KettlerExtendedBikeData>;
|
|
70
|
+
getStatus(): Promise<KettlerBikeData>;
|
|
71
|
+
getDB(): Promise<string>;
|
|
72
|
+
send(logStr: string, message: string, timeout?: any): Promise<any>;
|
|
73
|
+
parseExtendedStatus(data: string): KettlerExtendedBikeData;
|
|
74
|
+
parseStatus(data: string): KettlerBikeData;
|
|
75
|
+
check(): Promise<boolean>;
|
|
76
|
+
start(props?: any): Promise<any>;
|
|
77
|
+
startUpdatePull(): void;
|
|
78
|
+
stop(): Promise<boolean>;
|
|
79
|
+
pause(): Promise<boolean>;
|
|
80
|
+
resume(): Promise<boolean>;
|
|
81
|
+
mapData(bikeData: KettlerBikeData): IncyclistBikeData;
|
|
82
|
+
transformData(internalData: IncyclistBikeData, bikeData: KettlerBikeData): DeviceData;
|
|
83
|
+
update(): Promise<void>;
|
|
84
|
+
sendRequest(request: any): Promise<any>;
|
|
85
|
+
sendRequests(): Promise<void>;
|
|
86
|
+
bikeSync(): Promise<void>;
|
|
87
|
+
sendUpdate(request: any): Promise<unknown>;
|
|
88
|
+
sendData(): void;
|
|
89
|
+
refreshRequests(): void;
|
|
90
|
+
processClientRequest(request: any): Promise<unknown>;
|
|
91
|
+
waitForOpened(): Promise<boolean>;
|
|
92
|
+
waitForClosed(): Promise<boolean>;
|
|
93
|
+
getSupportedCyclingModes(): any[];
|
|
94
|
+
setCyclingMode(mode: CyclingMode | string, settings?: any): void;
|
|
95
|
+
getCyclingMode(): CyclingMode;
|
|
96
|
+
getDefaultCyclingMode(): CyclingMode;
|
|
97
|
+
setUserSettings(userSettings: any): void;
|
|
98
|
+
setBikeSettings(bikeSettings: any): void;
|
|
99
|
+
}
|