incyclist-devices 2.0.32 → 2.0.33
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.
|
@@ -190,9 +190,7 @@ class DaumPremiumAdapter extends DaumAdapter_1.default {
|
|
|
190
190
|
const { route, onStatusUpdate, gear } = props;
|
|
191
191
|
var info = {};
|
|
192
192
|
this.initData();
|
|
193
|
-
|
|
194
|
-
yield this.stop();
|
|
195
|
-
}
|
|
193
|
+
yield this.stop();
|
|
196
194
|
return (0, utils_1.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () {
|
|
197
195
|
try {
|
|
198
196
|
yield this.connect();
|
|
@@ -244,7 +242,7 @@ class DaumPremiumAdapter extends DaumAdapter_1.default {
|
|
|
244
242
|
if (!this.bike.isConnected()) {
|
|
245
243
|
const connected = yield this.bike.connect();
|
|
246
244
|
if (!connected)
|
|
247
|
-
|
|
245
|
+
throw new Error('not connected');
|
|
248
246
|
}
|
|
249
247
|
return this.getBike().getTrainingData();
|
|
250
248
|
});
|
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { ReservedCommands } from './utils';
|
|
3
3
|
import { SerialInterface, SerialPortProvider } from '../..';
|
|
4
|
-
import { Queue } from '../../../utils/utils';
|
|
5
4
|
import { EventLogger } from 'gd-eventlog';
|
|
6
5
|
import { User } from "../../../types/user";
|
|
7
6
|
import { Route } from "../../../types/route";
|
|
8
7
|
import { SerialCommProps } from "../../comm";
|
|
9
8
|
import { SerialPortStream } from "@serialport/stream";
|
|
10
9
|
import { OnDeviceStartCallback } from "./types";
|
|
10
|
+
import { Queue } from "../../../utils/utils";
|
|
11
|
+
export type ResponseObject = {
|
|
12
|
+
type: ResponseType;
|
|
13
|
+
data?: string;
|
|
14
|
+
error?: Error;
|
|
15
|
+
};
|
|
16
|
+
export type ResponseType = 'ACK' | 'NAK' | 'Response' | 'Error';
|
|
17
|
+
export type Daum8iCommsState = {
|
|
18
|
+
waitingForStart?: boolean;
|
|
19
|
+
waitingForACK?: boolean;
|
|
20
|
+
waitingForEnd?: boolean;
|
|
21
|
+
partialCmd?: any;
|
|
22
|
+
data: Queue<ResponseObject>;
|
|
23
|
+
};
|
|
24
|
+
export type ConnectionState = 'Connecting' | 'Connected' | 'Disconnected' | 'Disconnecting';
|
|
25
|
+
export declare class CheckSumError extends Error {
|
|
26
|
+
constructor();
|
|
27
|
+
}
|
|
28
|
+
export declare class ACKTimeout extends Error {
|
|
29
|
+
constructor();
|
|
30
|
+
}
|
|
31
|
+
export declare class BusyTimeout extends Error {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
export declare class ResponseTimeout extends Error {
|
|
35
|
+
constructor();
|
|
36
|
+
}
|
|
11
37
|
export default class Daum8i {
|
|
12
38
|
logger: EventLogger;
|
|
13
39
|
serial: SerialInterface;
|
|
@@ -18,17 +44,16 @@ export default class Daum8i {
|
|
|
18
44
|
};
|
|
19
45
|
port: string;
|
|
20
46
|
settings: any;
|
|
21
|
-
sp: SerialPortStream;
|
|
22
47
|
props: any;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
protected sp: SerialPortStream;
|
|
49
|
+
protected connectState: ConnectionState;
|
|
50
|
+
protected connectPromise: Promise<SerialPortStream>;
|
|
51
|
+
protected disconnectPromise: Promise<boolean>;
|
|
52
|
+
protected writePromise: Promise<void>;
|
|
53
|
+
protected sendCmdPromise: Promise<string>;
|
|
54
|
+
protected actualBikeType?: string;
|
|
55
|
+
protected recvState: Daum8iCommsState;
|
|
26
56
|
bikeData: any;
|
|
27
|
-
processor: any;
|
|
28
|
-
error: Error;
|
|
29
|
-
queue: Queue<any>;
|
|
30
|
-
cmdCurrent: any;
|
|
31
|
-
cmdStart: number;
|
|
32
57
|
isLoggingPaused: boolean;
|
|
33
58
|
spp: SerialPortProvider;
|
|
34
59
|
serialportProps: any;
|
|
@@ -40,29 +65,25 @@ export default class Daum8i {
|
|
|
40
65
|
resumeLogging(): void;
|
|
41
66
|
logEvent(e: any): void;
|
|
42
67
|
connect(): Promise<boolean>;
|
|
68
|
+
closePort(): Promise<boolean>;
|
|
69
|
+
cleanupPort(): void;
|
|
43
70
|
close(): Promise<boolean>;
|
|
44
71
|
flush(): Promise<void>;
|
|
45
|
-
getLogState(): {
|
|
46
|
-
sending: any;
|
|
47
|
-
busy: any;
|
|
48
|
-
writeBusy: any;
|
|
49
|
-
opening: any;
|
|
50
|
-
connecting: any;
|
|
51
|
-
waitingForStart: any;
|
|
52
|
-
waitingForEnd: any;
|
|
53
|
-
waitingForAck: any;
|
|
54
|
-
retry: any;
|
|
55
|
-
};
|
|
56
72
|
onPortClose(): Promise<void>;
|
|
57
|
-
onPortError(error:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
checkForResponse(): boolean;
|
|
73
|
+
onPortError(error: Error): Promise<void>;
|
|
74
|
+
isSending(): boolean;
|
|
75
|
+
rejectCurrent(error: Error): void;
|
|
61
76
|
getTimeoutValue(cmd?: any): number;
|
|
62
77
|
onData(data: any, depth?: number): any;
|
|
78
|
+
waitWithTimeout(promise: Promise<any>, timeout: number, onTimeout?: () => void): Promise<any>;
|
|
63
79
|
sendDaum8iCommand(command: string, payload?: string | any[]): Promise<string>;
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
onIgnored(payload: string): void;
|
|
81
|
+
onACK(): void;
|
|
82
|
+
onNAK: () => void;
|
|
83
|
+
waitForACK(): Promise<boolean>;
|
|
84
|
+
waitForResponse(): Promise<string>;
|
|
85
|
+
portWrite(buffer: Buffer): Promise<void>;
|
|
86
|
+
write(buffer: Buffer, ackExpected?: boolean): Promise<void>;
|
|
66
87
|
sendACK(): Promise<void>;
|
|
67
88
|
sendNAK(): Promise<void>;
|
|
68
89
|
sendReservedDaum8iCommand(command: ReservedCommands, data?: Buffer): Promise<any[]>;
|
|
@@ -9,9 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ResponseTimeout = exports.BusyTimeout = exports.ACKTimeout = exports.CheckSumError = void 0;
|
|
12
13
|
const constants_1 = require("../constants");
|
|
13
14
|
const utils_1 = require("./utils");
|
|
14
15
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
16
|
+
const utils_2 = require("../../../utils/utils");
|
|
15
17
|
const DEFAULT_TIMEOUT = 10000;
|
|
16
18
|
const MAX_DATA_BLOCK_SIZE = 512;
|
|
17
19
|
const DS_BITS_OFF = 0;
|
|
@@ -34,8 +36,38 @@ const validatePath = (path) => {
|
|
|
34
36
|
const port = parts[1];
|
|
35
37
|
return `${host}:${port}`;
|
|
36
38
|
};
|
|
39
|
+
class CheckSumError extends Error {
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
this.message = 'checksum incorrect';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.CheckSumError = CheckSumError;
|
|
46
|
+
class ACKTimeout extends Error {
|
|
47
|
+
constructor() {
|
|
48
|
+
super();
|
|
49
|
+
this.message = 'ACK timeout';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.ACKTimeout = ACKTimeout;
|
|
53
|
+
class BusyTimeout extends Error {
|
|
54
|
+
constructor() {
|
|
55
|
+
super();
|
|
56
|
+
this.message = 'BUSY timeout';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.BusyTimeout = BusyTimeout;
|
|
60
|
+
class ResponseTimeout extends Error {
|
|
61
|
+
constructor() {
|
|
62
|
+
super();
|
|
63
|
+
this.message = 'RESP timeout';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.ResponseTimeout = ResponseTimeout;
|
|
37
67
|
class Daum8i {
|
|
38
68
|
constructor(props) {
|
|
69
|
+
this.onNAK = () => {
|
|
70
|
+
};
|
|
39
71
|
this.props = props || {};
|
|
40
72
|
const { logger, serial, path } = props;
|
|
41
73
|
this.serial = serial;
|
|
@@ -43,12 +75,9 @@ class Daum8i {
|
|
|
43
75
|
const w = global.window;
|
|
44
76
|
this.logger = logger || ((w === null || w === void 0 ? void 0 : w.DEVICE_DEBUG) || process.env.DEBUG ? DEBUG_LOGGER : new gd_eventlog_1.EventLogger('DaumPremium'));
|
|
45
77
|
this.isLoggingPaused = false;
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
48
|
-
this.
|
|
49
|
-
ack: { wait: false, startWait: undefined },
|
|
50
|
-
commandsInQueue: {},
|
|
51
|
-
};
|
|
78
|
+
this.connectState = 'Disconnected';
|
|
79
|
+
this.connectPromise = null;
|
|
80
|
+
this.recvState = { data: new utils_2.Queue() };
|
|
52
81
|
this.settings = {};
|
|
53
82
|
this.bikeData = {
|
|
54
83
|
userWeight: 75,
|
|
@@ -64,7 +93,7 @@ class Daum8i {
|
|
|
64
93
|
return this.path;
|
|
65
94
|
}
|
|
66
95
|
isConnected() {
|
|
67
|
-
return this.
|
|
96
|
+
return this.connectState === 'Connected' || this.connectState === 'Disconnecting';
|
|
68
97
|
}
|
|
69
98
|
pauseLogging() {
|
|
70
99
|
this.isLoggingPaused = true;
|
|
@@ -86,159 +115,124 @@ class Daum8i {
|
|
|
86
115
|
if (this.isConnected() && this.sp) {
|
|
87
116
|
return true;
|
|
88
117
|
}
|
|
118
|
+
if (this.connectState === 'Connecting') {
|
|
119
|
+
if (this.connectPromise) {
|
|
120
|
+
try {
|
|
121
|
+
yield this.connectPromise;
|
|
122
|
+
}
|
|
123
|
+
catch (_a) {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return this.isConnected();
|
|
127
|
+
}
|
|
89
128
|
try {
|
|
90
|
-
|
|
129
|
+
this.connectState = 'Connecting';
|
|
130
|
+
this.connectPromise = this.serial.openPort(this.path);
|
|
131
|
+
const port = yield this.connectPromise;
|
|
132
|
+
this.connectPromise = null;
|
|
91
133
|
if (port !== null) {
|
|
92
|
-
this.
|
|
134
|
+
this.connectState = 'Connected';
|
|
93
135
|
this.sp = port;
|
|
94
136
|
this.sp.on('close', this.onPortClose.bind(this));
|
|
95
|
-
this.sp.on('error',
|
|
96
|
-
this.sp.on('data',
|
|
137
|
+
this.sp.on('error', this.onPortError.bind(this));
|
|
138
|
+
this.sp.on('data', this.onData.bind(this));
|
|
97
139
|
return true;
|
|
98
140
|
}
|
|
99
141
|
else {
|
|
142
|
+
this.connectState = 'Disconnected';
|
|
100
143
|
return false;
|
|
101
144
|
}
|
|
102
145
|
}
|
|
103
|
-
catch (
|
|
146
|
+
catch (_b) {
|
|
147
|
+
this.connectState = 'Disconnected';
|
|
104
148
|
return false;
|
|
105
149
|
}
|
|
106
150
|
});
|
|
107
151
|
}
|
|
152
|
+
closePort() {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
if (!this.sp)
|
|
155
|
+
return true;
|
|
156
|
+
try {
|
|
157
|
+
yield this.flush();
|
|
158
|
+
yield this.serial.closePort(this.path);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
this.logEvent({ message: 'could not close ', reason: err.message });
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
cleanupPort() {
|
|
168
|
+
if (this.sp) {
|
|
169
|
+
this.sp.removeAllListeners();
|
|
170
|
+
}
|
|
171
|
+
this.sp = null;
|
|
172
|
+
this.recvState.data.clear();
|
|
173
|
+
}
|
|
108
174
|
close() {
|
|
109
175
|
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
-
|
|
176
|
+
let isDisconnected = false;
|
|
177
|
+
if (this.disconnectPromise) {
|
|
111
178
|
try {
|
|
112
|
-
yield this.
|
|
113
|
-
yield this.serial.closePort(this.path);
|
|
179
|
+
isDisconnected = yield this.disconnectPromise;
|
|
114
180
|
}
|
|
115
|
-
catch (
|
|
116
|
-
this.logEvent({ message: 'could not close ', reason: err.message });
|
|
117
|
-
return false;
|
|
181
|
+
catch (_a) {
|
|
118
182
|
}
|
|
183
|
+
return isDisconnected;
|
|
119
184
|
}
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.sp = null;
|
|
185
|
+
if (this.connectState === 'Disconnected') {
|
|
186
|
+
this.cleanupPort();
|
|
187
|
+
return true;
|
|
124
188
|
}
|
|
125
|
-
|
|
189
|
+
else if (this.connectState === 'Disconnecting' || this.connectState === 'Connected' || this.connectState === 'Connecting') {
|
|
190
|
+
this.connectState = 'Disconnecting';
|
|
191
|
+
this.disconnectPromise = this.closePort();
|
|
192
|
+
isDisconnected = yield this.disconnectPromise;
|
|
193
|
+
this.connectPromise = null;
|
|
194
|
+
this.disconnectPromise = null;
|
|
195
|
+
if (isDisconnected)
|
|
196
|
+
this.connectState = 'Disconnected';
|
|
197
|
+
this.cleanupPort();
|
|
198
|
+
}
|
|
199
|
+
return isDisconnected;
|
|
126
200
|
});
|
|
127
201
|
}
|
|
128
202
|
flush() {
|
|
129
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (!this.state.writeBusy) {
|
|
135
|
-
clearInterval(iv);
|
|
136
|
-
this.writeDone();
|
|
137
|
-
done();
|
|
138
|
-
}
|
|
139
|
-
}, 100);
|
|
140
|
-
});
|
|
204
|
+
if (this.writePromise) {
|
|
205
|
+
yield this.waitWithTimeout(this.writePromise, 1000);
|
|
206
|
+
this.writePromise = null;
|
|
207
|
+
}
|
|
141
208
|
});
|
|
142
209
|
}
|
|
143
|
-
getLogState() {
|
|
144
|
-
let s = undefined;
|
|
145
|
-
const { sending, busy, opening, connecting, writeBusy, waitingForStart, waitingForAck, waitingForEnd, retry } = this.state;
|
|
146
|
-
if (sending) {
|
|
147
|
-
s = {};
|
|
148
|
-
s.command = sending.command;
|
|
149
|
-
s.payload = sending.payload;
|
|
150
|
-
}
|
|
151
|
-
return { sending: s, busy, writeBusy, opening, connecting, waitingForStart, waitingForEnd, waitingForAck, retry };
|
|
152
|
-
}
|
|
153
210
|
onPortClose() {
|
|
154
211
|
return __awaiter(this, void 0, void 0, function* () {
|
|
155
|
-
this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
212
|
+
if (this.connectState !== 'Disconnected' && this.connectState !== 'Disconnecting')
|
|
213
|
+
this.logEvent({ message: "port closed:", port: this.path });
|
|
214
|
+
this.connectState = 'Disconnected';
|
|
215
|
+
this.cleanupPort();
|
|
160
216
|
});
|
|
161
217
|
}
|
|
162
218
|
onPortError(error) {
|
|
163
219
|
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
-
|
|
165
|
-
this.error = error;
|
|
166
|
-
if (this.blocked) {
|
|
167
|
-
if (!this.state.closed) {
|
|
168
|
-
yield this.close();
|
|
169
|
-
}
|
|
220
|
+
if (this.connectState === 'Disconnecting' || this.connectState === 'Disconnected') {
|
|
170
221
|
return;
|
|
171
222
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.writeDone();
|
|
176
|
-
yield this.close();
|
|
177
|
-
return;
|
|
223
|
+
this.logEvent({ message: "port error:", port: this.path, error: error.message, connected: this.isConnected(), state: this.connectState });
|
|
224
|
+
if (this.isSending()) {
|
|
225
|
+
this.rejectCurrent(error);
|
|
178
226
|
}
|
|
179
|
-
this.
|
|
227
|
+
if (this.connectState === 'Connected')
|
|
228
|
+
this.close();
|
|
180
229
|
});
|
|
181
230
|
}
|
|
182
|
-
|
|
183
|
-
return
|
|
184
|
-
if (!this.sp)
|
|
185
|
-
return;
|
|
186
|
-
try {
|
|
187
|
-
yield this.close();
|
|
188
|
-
this.writeDone();
|
|
189
|
-
if (this.queue !== undefined)
|
|
190
|
-
this.queue.clear();
|
|
191
|
-
}
|
|
192
|
-
catch (_a) { }
|
|
193
|
-
this.connected = false;
|
|
194
|
-
if (updateState)
|
|
195
|
-
this.state = { opened: false, closed: true, busy: false };
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
sendTimeout(message) {
|
|
199
|
-
this.logEvent({ message: `sendCommand:${message || 'timeout'}`, port: this.path, cmd: this.cmdCurrent });
|
|
200
|
-
delete this.state.commandsInQueue[this.cmdCurrent.command];
|
|
201
|
-
if (this.cmdCurrent.callbackErr !== undefined) {
|
|
202
|
-
let cb = this.cmdCurrent.callbackErr;
|
|
203
|
-
this.state.busy = false;
|
|
204
|
-
this.cmdCurrent = undefined;
|
|
205
|
-
this.cmdStart = undefined;
|
|
206
|
-
cb(408, { message: message || "timeout" });
|
|
207
|
-
}
|
|
231
|
+
isSending() {
|
|
232
|
+
return (this.writePromise !== undefined && this.writePromise !== null) || (this.sendCmdPromise !== null && this.sendCmdPromise !== undefined);
|
|
208
233
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const s = this.state.sending;
|
|
212
|
-
if (s === undefined)
|
|
213
|
-
return false;
|
|
214
|
-
const rejectFn = s.reject;
|
|
215
|
-
const reject = (err) => {
|
|
216
|
-
if (rejectFn && typeof rejectFn === 'function') {
|
|
217
|
-
rejectFn(err);
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
const error = this.state.error;
|
|
221
|
-
if (error !== undefined) {
|
|
222
|
-
reject(error);
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
try {
|
|
226
|
-
if (this.state.waitingForACK) {
|
|
227
|
-
const timeoutACK = this.state.ack ? this.state.ack.timeout : this.state.sending.timeout;
|
|
228
|
-
if (d < timeoutACK)
|
|
229
|
-
return true;
|
|
230
|
-
reject(new Error('ACK timeout'));
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
if (d < this.state.sending.timeout)
|
|
234
|
-
return true;
|
|
235
|
-
reject(new Error('RESP timeout'));
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
this.logEvent({ message: 'checkForResponse: Exception', port: this.path, error: err.message, stack: err.stack });
|
|
240
|
-
}
|
|
241
|
-
return true;
|
|
234
|
+
rejectCurrent(error) {
|
|
235
|
+
this.recvState.data.enqueue({ type: 'Error', error });
|
|
242
236
|
}
|
|
243
237
|
getTimeoutValue(cmd) {
|
|
244
238
|
let timeout = DEFAULT_TIMEOUT;
|
|
@@ -253,18 +247,13 @@ class Daum8i {
|
|
|
253
247
|
return __awaiter(this, void 0, void 0, function* () {
|
|
254
248
|
let cmd = '';
|
|
255
249
|
const MAX_DEPTH = 5;
|
|
256
|
-
if (this.
|
|
257
|
-
cmd = this.
|
|
250
|
+
if (this.recvState.waitingForEnd) {
|
|
251
|
+
cmd = this.recvState.partialCmd;
|
|
258
252
|
}
|
|
259
253
|
const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, 'latin1');
|
|
260
|
-
const s = this.state.sending;
|
|
261
|
-
if (s === undefined) {
|
|
262
|
-
this.logEvent({ message: 'onData:IGNORED', data: bufferData.toString('hex') });
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const { portName, resolve } = this.state.sending;
|
|
266
254
|
let incoming = bufferData;
|
|
267
|
-
|
|
255
|
+
if (depth === 0)
|
|
256
|
+
this.logEvent({ message: 'sendCommand:RECV', data: (0, utils_1.hexstr)(incoming), state: this.recvState });
|
|
268
257
|
for (let i = 0; i < incoming.length; i++) {
|
|
269
258
|
const getRemaining = () => {
|
|
270
259
|
let remaining = '';
|
|
@@ -277,179 +266,222 @@ class Daum8i {
|
|
|
277
266
|
};
|
|
278
267
|
const c = incoming.readUInt8(i);
|
|
279
268
|
if (c === 0x06) {
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
269
|
+
this.recvState.waitingForStart = true;
|
|
270
|
+
this.recvState.waitingForACK = false;
|
|
282
271
|
const remaining = getRemaining();
|
|
283
|
-
this.
|
|
272
|
+
this.recvState.data.enqueue({ type: 'ACK' });
|
|
284
273
|
if (remaining && remaining !== '' && depth < MAX_DEPTH)
|
|
285
274
|
return this.onData(remaining, depth + 1);
|
|
286
275
|
}
|
|
287
276
|
else if (c === 0x15) {
|
|
288
|
-
this.
|
|
289
|
-
this.
|
|
277
|
+
this.recvState.waitingForStart = true;
|
|
278
|
+
this.recvState.waitingForACK = false;
|
|
290
279
|
const remaining = getRemaining();
|
|
291
|
-
this.
|
|
280
|
+
this.recvState.data.enqueue({ type: 'NAK' });
|
|
292
281
|
if (remaining && remaining !== '' && depth < MAX_DEPTH)
|
|
293
282
|
return this.onData(remaining, depth + 1);
|
|
294
283
|
}
|
|
295
284
|
else if (c === 0x01) {
|
|
296
|
-
this.
|
|
285
|
+
this.recvState.waitingForStart = false;
|
|
286
|
+
this.recvState.waitingForEnd = true;
|
|
297
287
|
}
|
|
298
288
|
else if (c === 0x17) {
|
|
299
289
|
const remaining = getRemaining();
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
290
|
+
this.recvState.waitingForEnd = false;
|
|
291
|
+
const cmdStr = cmd.substring(0, cmd.length - 2);
|
|
292
|
+
const checksumExtracted = cmd.slice(-2);
|
|
293
|
+
const checksumCalculated = (0, utils_1.checkSum)((0, utils_1.getAsciiArrayFromStr)(cmdStr), []);
|
|
294
|
+
if (checksumExtracted === checksumCalculated) {
|
|
295
|
+
const payload = cmd.substring(3, cmd.length - 2);
|
|
296
|
+
this.recvState.data.enqueue({ type: 'Response', data: payload });
|
|
303
297
|
}
|
|
304
298
|
else {
|
|
305
|
-
|
|
306
|
-
this.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (checksumExtracted === checksumCalculated) {
|
|
311
|
-
yield this.sendACK();
|
|
312
|
-
if (this.state.sending && this.state.sending.responseCheckIv) {
|
|
313
|
-
clearInterval(this.state.sending.responseCheckIv);
|
|
314
|
-
}
|
|
315
|
-
this.state = {
|
|
316
|
-
sending: undefined,
|
|
317
|
-
busy: false,
|
|
318
|
-
writeBusy: false,
|
|
319
|
-
waitingForStart: false,
|
|
320
|
-
waitingForEnd: false,
|
|
321
|
-
waitingForACK: false,
|
|
322
|
-
};
|
|
323
|
-
const payload = cmd.substring(3, cmd.length - 2);
|
|
324
|
-
resolve(payload);
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
yield this.sendNAK();
|
|
328
|
-
}
|
|
299
|
+
const error = new CheckSumError();
|
|
300
|
+
this.recvState.data.enqueue({ type: 'Error', error, data: cmd });
|
|
301
|
+
this.recvState.waitingForACK = false;
|
|
302
|
+
this.recvState.waitingForStart = true;
|
|
303
|
+
this.recvState.waitingForEnd = false;
|
|
329
304
|
}
|
|
330
305
|
cmd = '';
|
|
331
306
|
if (remaining && depth < 5)
|
|
332
307
|
return this.onData(remaining, depth + 1);
|
|
333
308
|
}
|
|
334
309
|
else {
|
|
335
|
-
if (this.
|
|
310
|
+
if (this.recvState.waitingForEnd)
|
|
336
311
|
cmd += String.fromCharCode(c);
|
|
337
312
|
}
|
|
338
313
|
}
|
|
339
|
-
if (this.
|
|
340
|
-
this.
|
|
314
|
+
if (this.recvState.waitingForEnd) {
|
|
315
|
+
this.recvState.partialCmd = cmd;
|
|
341
316
|
}
|
|
342
317
|
});
|
|
343
318
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return
|
|
349
|
-
|
|
350
|
-
|
|
319
|
+
waitWithTimeout(promise, timeout, onTimeout) {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
let to;
|
|
322
|
+
const toPromise = (ms) => {
|
|
323
|
+
return new Promise(resolve => { to = setTimeout(resolve, ms); });
|
|
324
|
+
};
|
|
325
|
+
let res;
|
|
326
|
+
try {
|
|
327
|
+
res = yield Promise.race([promise, toPromise(timeout).then(() => { if (onTimeout)
|
|
328
|
+
onTimeout(); })]);
|
|
351
329
|
}
|
|
352
|
-
|
|
353
|
-
|
|
330
|
+
catch (_a) {
|
|
331
|
+
}
|
|
332
|
+
clearTimeout(to);
|
|
333
|
+
return res;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
sendDaum8iCommand(command, payload = '') {
|
|
337
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
338
|
+
const message = (0, utils_1.buildMessage)(command, payload);
|
|
339
|
+
const tsRequest = Date.now();
|
|
340
|
+
if (this.sendCmdPromise) {
|
|
354
341
|
this.logEvent({ message: 'sendCommand:waiting', port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message) });
|
|
355
|
-
const
|
|
356
|
-
return new Promise((done) => {
|
|
357
|
-
let start = Date.now();
|
|
358
|
-
let timeout = start + 5000;
|
|
359
|
-
const iv = setInterval(() => {
|
|
360
|
-
if (this.state.busy) {
|
|
361
|
-
if (Date.now() > timeout) {
|
|
362
|
-
clearInterval(iv);
|
|
363
|
-
done(false);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
clearInterval(iv);
|
|
368
|
-
done(true);
|
|
369
|
-
}
|
|
370
|
-
}, 10);
|
|
371
|
-
});
|
|
372
|
-
};
|
|
373
|
-
const res = yield busyWait();
|
|
374
|
-
if (!res) {
|
|
342
|
+
const onTimeout = () => {
|
|
375
343
|
this.logEvent({ message: 'sendCommand:busy timeout', port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message), duration: Date.now() - tsRequest });
|
|
376
|
-
|
|
344
|
+
throw new Error('BUSY timeout');
|
|
345
|
+
};
|
|
346
|
+
this.waitWithTimeout(this.sendCmdPromise, 5000, onTimeout);
|
|
347
|
+
this.sendCmdPromise = null;
|
|
348
|
+
}
|
|
349
|
+
this.sendCmdPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
350
|
+
try {
|
|
351
|
+
this.logEvent({ message: "sendCommand:sending:", port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message) });
|
|
352
|
+
if (!this.isConnected()) {
|
|
353
|
+
const connected = yield this.connect();
|
|
354
|
+
if (!connected) {
|
|
355
|
+
reject(new Error('not connected'));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
let retryCnt = 0;
|
|
360
|
+
let ok = false;
|
|
361
|
+
do {
|
|
362
|
+
yield this.write(Buffer.from(message));
|
|
363
|
+
ok = yield this.waitForACK();
|
|
364
|
+
if (!ok) {
|
|
365
|
+
yield (0, utils_2.sleep)(1000);
|
|
366
|
+
retryCnt++;
|
|
367
|
+
}
|
|
368
|
+
} while (!ok && retryCnt < 5);
|
|
369
|
+
const res = yield this.waitForResponse();
|
|
370
|
+
this.sendCmdPromise = null;
|
|
371
|
+
resolve(res);
|
|
372
|
+
}
|
|
373
|
+
catch (err) {
|
|
374
|
+
this.logEvent({ message: "sendCommand:error:", port: this.path, error: err.message, stack: err.stack });
|
|
375
|
+
this.sendCmdPromise = null;
|
|
376
|
+
reject(err);
|
|
377
|
+
}
|
|
378
|
+
}));
|
|
379
|
+
return this.sendCmdPromise;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
onIgnored(payload) {
|
|
383
|
+
this.logEvent({ message: 'onData:IGNORED', port: this.path, data: payload });
|
|
384
|
+
}
|
|
385
|
+
onACK() {
|
|
386
|
+
}
|
|
387
|
+
waitForACK() {
|
|
388
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
389
|
+
this.recvState.waitingForACK = true;
|
|
390
|
+
const timeout = this.getTimeoutValue();
|
|
391
|
+
let waitingForACK = true;
|
|
392
|
+
let start = Date.now();
|
|
393
|
+
let tsTimeout = start + timeout;
|
|
394
|
+
while (waitingForACK && Date.now() < tsTimeout) {
|
|
395
|
+
const response = this.recvState.data.dequeue();
|
|
396
|
+
if (!response) {
|
|
397
|
+
yield (0, utils_2.sleep)(5);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
if (response.type === 'ACK' || response.type === 'NAK') {
|
|
401
|
+
this.logEvent({ message: `sendCommand:${response.type} received:`, port: this.path });
|
|
402
|
+
waitingForACK = false;
|
|
403
|
+
return (response.type === 'ACK');
|
|
404
|
+
}
|
|
377
405
|
}
|
|
378
|
-
this.state.busy = true;
|
|
379
406
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
407
|
+
throw new ACKTimeout();
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
waitForResponse() {
|
|
411
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
412
|
+
const timeout = this.getTimeoutValue();
|
|
413
|
+
let waitingForResponse = true;
|
|
414
|
+
let start = Date.now();
|
|
415
|
+
let tsTimeout = start + timeout;
|
|
416
|
+
let retry = 0;
|
|
417
|
+
while (waitingForResponse && Date.now() < tsTimeout && retry < 5) {
|
|
418
|
+
const response = this.recvState.data.dequeue();
|
|
419
|
+
if (!response) {
|
|
420
|
+
yield (0, utils_2.sleep)(5);
|
|
393
421
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
422
|
+
else {
|
|
423
|
+
if (response.type === 'Response') {
|
|
424
|
+
this.logEvent({ message: `sendCommand:received:`, port: this.path, cmd: response.data });
|
|
425
|
+
yield this.sendACK();
|
|
426
|
+
waitingForResponse = false;
|
|
427
|
+
return response.data;
|
|
428
|
+
}
|
|
429
|
+
if (response.type === 'Error') {
|
|
430
|
+
this.logEvent({ message: `sendCommand:received:ERROR`, port: this.path, error: response.error.message });
|
|
431
|
+
if (response.error instanceof CheckSumError && retry < 5) {
|
|
432
|
+
yield this.sendNAK();
|
|
433
|
+
retry++;
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
throw response.error;
|
|
437
|
+
}
|
|
405
438
|
}
|
|
406
|
-
}
|
|
407
|
-
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
throw new ResponseTimeout();
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
portWrite(buffer) {
|
|
445
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
446
|
+
if (!this.sp) {
|
|
447
|
+
this.logEvent({ message: 'write failed', error: 'port is not opened' });
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
yield this.sp.write(buffer);
|
|
408
452
|
}
|
|
409
453
|
catch (err) {
|
|
410
|
-
this.logEvent({ message:
|
|
411
|
-
this.writeDone();
|
|
412
|
-
reject(err);
|
|
454
|
+
this.logEvent({ message: 'write failed', error: err.message });
|
|
413
455
|
}
|
|
414
|
-
})
|
|
415
|
-
}
|
|
416
|
-
writeDone() {
|
|
417
|
-
this.state.writeBusy = false;
|
|
418
|
-
this.state.busy = false;
|
|
419
|
-
if (this.state.sending && this.state.sending.responseCheckIv) {
|
|
420
|
-
clearInterval(this.state.sending.responseCheckIv);
|
|
421
|
-
}
|
|
422
|
-
this.state.sending = undefined;
|
|
423
|
-
this.state.waitingForStart = false;
|
|
424
|
-
this.state.waitingForEnd = false;
|
|
425
|
-
this.state.waitingForACK = false;
|
|
456
|
+
});
|
|
426
457
|
}
|
|
427
|
-
write(buffer) {
|
|
458
|
+
write(buffer, ackExpected = true) {
|
|
428
459
|
return __awaiter(this, void 0, void 0, function* () {
|
|
429
|
-
|
|
430
|
-
this.state.writeBusy = true;
|
|
460
|
+
if (this.writePromise) {
|
|
431
461
|
try {
|
|
432
|
-
yield this.
|
|
433
|
-
this.state.writeBusy = false;
|
|
434
|
-
done();
|
|
462
|
+
yield this.writePromise;
|
|
435
463
|
}
|
|
436
|
-
catch (
|
|
437
|
-
this.state.writeBusy = false;
|
|
438
|
-
done();
|
|
464
|
+
catch (_a) {
|
|
439
465
|
}
|
|
440
|
-
|
|
466
|
+
this.writePromise = null;
|
|
467
|
+
}
|
|
468
|
+
this.writePromise = this.portWrite(buffer);
|
|
469
|
+
if (ackExpected)
|
|
470
|
+
this.recvState.waitingForACK = true;
|
|
471
|
+
yield this.writePromise;
|
|
472
|
+
this.writePromise = null;
|
|
441
473
|
});
|
|
442
474
|
}
|
|
443
475
|
sendACK() {
|
|
444
476
|
return __awaiter(this, void 0, void 0, function* () {
|
|
445
|
-
this.logEvent({ message: "sendCommand:sending ACK", port: this.path
|
|
446
|
-
yield this.write(Buffer.from([0x06]));
|
|
477
|
+
this.logEvent({ message: "sendCommand:sending ACK", port: this.path });
|
|
478
|
+
yield this.write(Buffer.from([0x06]), false);
|
|
447
479
|
});
|
|
448
480
|
}
|
|
449
481
|
sendNAK() {
|
|
450
482
|
return __awaiter(this, void 0, void 0, function* () {
|
|
451
|
-
this.logEvent({ message: "sendCommand:sending NAK", port: this.path
|
|
452
|
-
yield this.write(Buffer.from([0x15]));
|
|
483
|
+
this.logEvent({ message: "sendCommand:sending NAK", port: this.path });
|
|
484
|
+
yield this.write(Buffer.from([0x15]), false);
|
|
453
485
|
});
|
|
454
486
|
}
|
|
455
487
|
sendReservedDaum8iCommand(command, data) {
|
|
@@ -511,7 +543,7 @@ class Daum8i {
|
|
|
511
543
|
else {
|
|
512
544
|
throw (new Error(`unknown actual device type ${typeof str === 'string' ? (0, utils_1.ascii)(str.charAt(0)) : str}`));
|
|
513
545
|
}
|
|
514
|
-
this.
|
|
546
|
+
this.actualBikeType = deviceType;
|
|
515
547
|
return deviceType;
|
|
516
548
|
});
|
|
517
549
|
}
|
|
@@ -544,7 +576,7 @@ class Daum8i {
|
|
|
544
576
|
deviceType = constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
|
|
545
577
|
else
|
|
546
578
|
throw (new Error('unknown actual device type'));
|
|
547
|
-
this.
|
|
579
|
+
this.actualBikeType = deviceType;
|
|
548
580
|
return deviceType;
|
|
549
581
|
});
|
|
550
582
|
}
|