@robdobsn/raftjs 1.1.1
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/.editorconfig +14 -0
- package/.gitattributes +11 -0
- package/.nvmrc +1 -0
- package/LICENSE +22 -0
- package/README.md +11 -0
- package/TODO.md +1 -0
- package/dist/RaftAttributeHandler.d.ts +12 -0
- package/dist/RaftAttributeHandler.js +241 -0
- package/dist/RaftAttributeHandler.js.map +1 -0
- package/dist/RaftChannel.d.ts +18 -0
- package/dist/RaftChannel.js +12 -0
- package/dist/RaftChannel.js.map +1 -0
- package/dist/RaftChannelWebBLE.d.ts +38 -0
- package/dist/RaftChannelWebBLE.js +274 -0
- package/dist/RaftChannelWebBLE.js.map +1 -0
- package/dist/RaftChannelWebSerial.d.ts +37 -0
- package/dist/RaftChannelWebSerial.js +319 -0
- package/dist/RaftChannelWebSerial.js.map +1 -0
- package/dist/RaftChannelWebSocket.d.ts +28 -0
- package/dist/RaftChannelWebSocket.js +197 -0
- package/dist/RaftChannelWebSocket.js.map +1 -0
- package/dist/RaftCommsStats.d.ts +39 -0
- package/dist/RaftCommsStats.js +128 -0
- package/dist/RaftCommsStats.js.map +1 -0
- package/dist/RaftConnEvents.d.ts +31 -0
- package/dist/RaftConnEvents.js +44 -0
- package/dist/RaftConnEvents.js.map +1 -0
- package/dist/RaftConnector.d.ts +242 -0
- package/dist/RaftConnector.js +613 -0
- package/dist/RaftConnector.js.map +1 -0
- package/dist/RaftCustomAttrHandler.d.ts +4 -0
- package/dist/RaftCustomAttrHandler.js +50 -0
- package/dist/RaftCustomAttrHandler.js.map +1 -0
- package/dist/RaftDeviceInfo.d.ts +64 -0
- package/dist/RaftDeviceInfo.js +36 -0
- package/dist/RaftDeviceInfo.js.map +1 -0
- package/dist/RaftDeviceManager.d.ts +37 -0
- package/dist/RaftDeviceManager.js +450 -0
- package/dist/RaftDeviceManager.js.map +1 -0
- package/dist/RaftDeviceMsg.d.ts +9 -0
- package/dist/RaftDeviceMsg.js +11 -0
- package/dist/RaftDeviceMsg.js.map +1 -0
- package/dist/RaftDeviceStates.d.ts +33 -0
- package/dist/RaftDeviceStates.js +60 -0
- package/dist/RaftDeviceStates.js.map +1 -0
- package/dist/RaftFileHandler.d.ts +52 -0
- package/dist/RaftFileHandler.js +502 -0
- package/dist/RaftFileHandler.js.map +1 -0
- package/dist/RaftLog.d.ts +22 -0
- package/dist/RaftLog.js +63 -0
- package/dist/RaftLog.js.map +1 -0
- package/dist/RaftMiniHDLC.d.ts +18 -0
- package/dist/RaftMiniHDLC.js +383 -0
- package/dist/RaftMiniHDLC.js.map +1 -0
- package/dist/RaftMsgHandler.d.ts +57 -0
- package/dist/RaftMsgHandler.js +480 -0
- package/dist/RaftMsgHandler.js.map +1 -0
- package/dist/RaftMsgTrackInfo.d.ts +17 -0
- package/dist/RaftMsgTrackInfo.js +42 -0
- package/dist/RaftMsgTrackInfo.js.map +1 -0
- package/dist/RaftProtocolDefs.d.ts +30 -0
- package/dist/RaftProtocolDefs.js +48 -0
- package/dist/RaftProtocolDefs.js.map +1 -0
- package/dist/RaftStreamHandler.d.ts +38 -0
- package/dist/RaftStreamHandler.js +257 -0
- package/dist/RaftStreamHandler.js.map +1 -0
- package/dist/RaftSystemType.d.ts +21 -0
- package/dist/RaftSystemType.js +3 -0
- package/dist/RaftSystemType.js.map +1 -0
- package/dist/RaftSystemUtils.d.ts +136 -0
- package/dist/RaftSystemUtils.js +410 -0
- package/dist/RaftSystemUtils.js.map +1 -0
- package/dist/RaftTypes.d.ts +184 -0
- package/dist/RaftTypes.js +157 -0
- package/dist/RaftTypes.js.map +1 -0
- package/dist/RaftUpdateEvents.d.ts +33 -0
- package/dist/RaftUpdateEvents.js +46 -0
- package/dist/RaftUpdateEvents.js.map +1 -0
- package/dist/RaftUpdateManager.d.ts +61 -0
- package/dist/RaftUpdateManager.js +618 -0
- package/dist/RaftUpdateManager.js.map +1 -0
- package/dist/RaftUtils.d.ts +125 -0
- package/dist/RaftUtils.js +454 -0
- package/dist/RaftUtils.js.map +1 -0
- package/dist/RaftWifiTypes.d.ts +23 -0
- package/dist/RaftWifiTypes.js +43 -0
- package/dist/RaftWifiTypes.js.map +1 -0
- package/dist/TestDataGen.d.ts +7 -0
- package/dist/TestDataGen.js +133 -0
- package/dist/TestDataGen.js.map +1 -0
- package/dist/main.d.ts +18 -0
- package/dist/main.js +42 -0
- package/dist/main.js.map +1 -0
- package/eslint.config.mjs +33 -0
- package/examples/dashboard/package.json +39 -0
- package/examples/dashboard/src/ConnManager.ts +86 -0
- package/examples/dashboard/src/Main.tsx +100 -0
- package/examples/dashboard/src/StatusScreen.tsx +72 -0
- package/examples/dashboard/src/SystemTypeCog/CogStateInfo.ts +144 -0
- package/examples/dashboard/src/SystemTypeCog/SystemTypeCog.ts +77 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOn.ts +70 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOnBase.ts +33 -0
- package/examples/dashboard/src/SystemTypeMarty/RICAddOnManager.ts +342 -0
- package/examples/dashboard/src/SystemTypeMarty/RICCommsStats.ts +170 -0
- package/examples/dashboard/src/SystemTypeMarty/RICHWElem.ts +123 -0
- package/examples/dashboard/src/SystemTypeMarty/RICLEDPatternChecker.ts +207 -0
- package/examples/dashboard/src/SystemTypeMarty/RICROSSerial.ts +464 -0
- package/examples/dashboard/src/SystemTypeMarty/RICServoFaultDetector.ts +146 -0
- package/examples/dashboard/src/SystemTypeMarty/RICStateInfo.ts +32 -0
- package/examples/dashboard/src/SystemTypeMarty/RICSystemUtils.ts +371 -0
- package/examples/dashboard/src/SystemTypeMarty/RICTypes.ts +20 -0
- package/examples/dashboard/src/SystemTypeMarty/SystemTypeMarty.ts +113 -0
- package/examples/dashboard/src/index.html +15 -0
- package/examples/dashboard/src/index.tsx +15 -0
- package/examples/dashboard/src/styles.css +122 -0
- package/examples/dashboard/tsconfig.json +18 -0
- package/jest.config.js +11 -0
- package/package.json +50 -0
- package/src/RaftAttributeHandler.ts +289 -0
- package/src/RaftChannel.ts +30 -0
- package/src/RaftChannelWebBLE.ts +342 -0
- package/src/RaftChannelWebSerial.ts +408 -0
- package/src/RaftChannelWebSocket.ts +245 -0
- package/src/RaftCommsStats.ts +142 -0
- package/src/RaftConnEvents.ts +46 -0
- package/src/RaftConnector.ts +745 -0
- package/src/RaftCustomAttrHandler.ts +54 -0
- package/src/RaftDeviceInfo.ts +104 -0
- package/src/RaftDeviceManager.ts +542 -0
- package/src/RaftDeviceMsg.ts +20 -0
- package/src/RaftDeviceStates.ts +89 -0
- package/src/RaftFileHandler.ts +668 -0
- package/src/RaftLog.ts +70 -0
- package/src/RaftMiniHDLC.ts +396 -0
- package/src/RaftMsgHandler.ts +778 -0
- package/src/RaftMsgTrackInfo.ts +51 -0
- package/src/RaftProtocolDefs.ts +46 -0
- package/src/RaftStreamHandler.ts +328 -0
- package/src/RaftSystemType.ts +25 -0
- package/src/RaftSystemUtils.ts +487 -0
- package/src/RaftTypes.ts +250 -0
- package/src/RaftUpdateEvents.ts +48 -0
- package/src/RaftUpdateManager.ts +778 -0
- package/src/RaftUtils.ts +484 -0
- package/src/RaftWifiTypes.ts +36 -0
- package/src/TestDataGen.ts +157 -0
- package/src/main.ts +28 -0
- package/testdata/TestDeviceTypeRecs.json +492 -0
- package/tsconfig.json +27 -0
package/src/RaftUtils.ts
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// RaftUtils
|
|
4
|
+
// Part of RaftJS
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson & Chris Greening 2020-2024
|
|
7
|
+
// (C) 2020-2024 All rights reserved
|
|
8
|
+
//
|
|
9
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
|
|
11
|
+
import RaftLog from "./RaftLog";
|
|
12
|
+
import semverGt from "semver/functions/gt";
|
|
13
|
+
import semverEq from "semver/functions/eq";
|
|
14
|
+
|
|
15
|
+
export default class RaftUtils {
|
|
16
|
+
static _isEndianSet = false;
|
|
17
|
+
static _isLittleEndian = false;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* Add a string to a Uint8Array buffer
|
|
22
|
+
*
|
|
23
|
+
* @param buffer - buffer to add to
|
|
24
|
+
* @param strToAdd - string to be added
|
|
25
|
+
* @param startPos - start position in buffer (i.e. offset to place string at)
|
|
26
|
+
*/
|
|
27
|
+
static addStringToBuffer(
|
|
28
|
+
buffer: Uint8Array,
|
|
29
|
+
strToAdd: string,
|
|
30
|
+
startPos: number
|
|
31
|
+
): void {
|
|
32
|
+
// Check valid
|
|
33
|
+
if (buffer.length < startPos + strToAdd.length + 1) {
|
|
34
|
+
RaftLog.error("addStringToBuffer buffer too short");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let curPos = startPos;
|
|
38
|
+
for (let i = 0; i < strToAdd.length; i++) {
|
|
39
|
+
let charAt = strToAdd.charCodeAt(i);
|
|
40
|
+
if (charAt > 255) charAt = 255;
|
|
41
|
+
buffer.set([charAt], curPos++);
|
|
42
|
+
}
|
|
43
|
+
buffer.set([0], buffer.length - 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* Get a string from a Uint8Array buffer
|
|
49
|
+
*
|
|
50
|
+
* @param buffer - buffer to get from
|
|
51
|
+
* @param startPos - start position in buffer (i.e. offset to start of string at)
|
|
52
|
+
* @param strLen - length of string to get
|
|
53
|
+
* @returns strGot - string got from buffer
|
|
54
|
+
*/
|
|
55
|
+
static getStringFromBuffer(
|
|
56
|
+
buffer: Uint8Array,
|
|
57
|
+
startPos: number,
|
|
58
|
+
strLen: number
|
|
59
|
+
): string {
|
|
60
|
+
// Check valid
|
|
61
|
+
if (buffer.length < startPos + strLen) {
|
|
62
|
+
strLen = buffer.length - startPos;
|
|
63
|
+
if (strLen <= 0) return "";
|
|
64
|
+
}
|
|
65
|
+
let curPos = startPos;
|
|
66
|
+
let outStr = "";
|
|
67
|
+
for (let i = 0; i < strLen; i++) {
|
|
68
|
+
outStr += String.fromCharCode(buffer[curPos++]);
|
|
69
|
+
}
|
|
70
|
+
return outStr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* Debug code to format a Uint8Array to string for logging
|
|
76
|
+
*
|
|
77
|
+
* @param buffer - Uint8Array to be converted to hex string
|
|
78
|
+
*/
|
|
79
|
+
static bufferToHex(buffer: Uint8Array | null): string {
|
|
80
|
+
if (buffer == null) return "null";
|
|
81
|
+
return Array.from(new Uint8Array(buffer))
|
|
82
|
+
.map((b) => RaftUtils.padStartFn(b.toString(16), 2, "0"))
|
|
83
|
+
.join("");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
*
|
|
88
|
+
* Extract a big-endian float from a uint8array
|
|
89
|
+
*
|
|
90
|
+
* @param buf - Uint8Array containing float
|
|
91
|
+
* @returns float
|
|
92
|
+
*/
|
|
93
|
+
static getBEFloatFromBuf(buf: Uint8Array): number {
|
|
94
|
+
const revBuf = new Uint8Array(4);
|
|
95
|
+
if (RaftUtils.isLittleEndian()) {
|
|
96
|
+
for (let i = 0; i < 4; i++) revBuf[3 - i] = buf[i];
|
|
97
|
+
} else {
|
|
98
|
+
for (let i = 0; i < 4; i++) revBuf[i] = buf[i];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const floatVal = new Float32Array(revBuf.buffer);
|
|
102
|
+
// RaftUtils.debug('revFloat ' + RaftUtils.bufferToHex(revBuf) + floatVal[0]);
|
|
103
|
+
return floatVal[0];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Extract a big-endian uint64 from a uint8array
|
|
108
|
+
*
|
|
109
|
+
* @param buf - Uint8Array containing uint64
|
|
110
|
+
* @param pos - position (offset in buf) to get from
|
|
111
|
+
* @returns uint64
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
static getBEUint64FromBuf(buf: Uint8Array, bufPos: number): number {
|
|
115
|
+
if (RaftUtils.isLittleEndian())
|
|
116
|
+
return (
|
|
117
|
+
(buf[bufPos] << 56) +
|
|
118
|
+
(buf[bufPos + 1] << 48) +
|
|
119
|
+
(buf[bufPos + 2] << 40) +
|
|
120
|
+
(buf[bufPos + 3] << 32) +
|
|
121
|
+
(buf[bufPos + 4] << 24) +
|
|
122
|
+
(buf[bufPos + 5] << 16) +
|
|
123
|
+
(buf[bufPos + 6] << 8) +
|
|
124
|
+
buf[bufPos + 7]
|
|
125
|
+
);
|
|
126
|
+
return (
|
|
127
|
+
(buf[bufPos + 7] << 56) +
|
|
128
|
+
(buf[bufPos + 6] << 48) +
|
|
129
|
+
(buf[bufPos + 5] << 40) +
|
|
130
|
+
(buf[bufPos + 4] << 32) +
|
|
131
|
+
(buf[bufPos + 3] << 24) +
|
|
132
|
+
(buf[bufPos + 2] << 16) +
|
|
133
|
+
(buf[bufPos + 1] << 8) +
|
|
134
|
+
buf[bufPos]
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
*
|
|
140
|
+
* Extract a big-endian uint32 from a uint8array
|
|
141
|
+
*
|
|
142
|
+
* @param buf - Uint8Array containing uint32
|
|
143
|
+
* @param pos - position (offset in buf) to get from
|
|
144
|
+
* @returns uint32
|
|
145
|
+
*/
|
|
146
|
+
static getBEUint32FromBuf(buf: Uint8Array, bufPos: number): number {
|
|
147
|
+
if (RaftUtils.isLittleEndian())
|
|
148
|
+
return (
|
|
149
|
+
(buf[bufPos] << 24) +
|
|
150
|
+
(buf[bufPos + 1] << 16) +
|
|
151
|
+
(buf[bufPos + 2] << 8) +
|
|
152
|
+
buf[bufPos + 3]
|
|
153
|
+
);
|
|
154
|
+
return (
|
|
155
|
+
(buf[bufPos + 3] << 24) +
|
|
156
|
+
(buf[bufPos + 2] << 16) +
|
|
157
|
+
(buf[bufPos + 1] << 8) +
|
|
158
|
+
buf[bufPos]
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
*
|
|
164
|
+
* Extract a big-endian int16 from a uint8array
|
|
165
|
+
*
|
|
166
|
+
* @param buf - Uint8Array containing int16
|
|
167
|
+
* @param pos - position (offset in buf) to get from
|
|
168
|
+
* @returns int16
|
|
169
|
+
*/
|
|
170
|
+
static getBEInt16FromBuf(buf: Uint8Array, bufPos: number): number {
|
|
171
|
+
if (RaftUtils.isLittleEndian()) {
|
|
172
|
+
const val = buf[bufPos] * 256 + buf[bufPos + 1];
|
|
173
|
+
return val > 32767 ? val - 65536 : val;
|
|
174
|
+
}
|
|
175
|
+
const val = buf[bufPos + 1] * 256 + buf[bufPos];
|
|
176
|
+
return val > 32767 ? val - 65536 : val;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
*
|
|
181
|
+
* Extract a big-endian uint16 from a uint8array
|
|
182
|
+
*
|
|
183
|
+
* @param buf - Uint8Array containing uint16
|
|
184
|
+
* @param pos - position (offset in buf) to get from
|
|
185
|
+
* @returns int16
|
|
186
|
+
*/
|
|
187
|
+
static getBEUint16FromBuf(buf: Uint8Array, bufPos: number): number {
|
|
188
|
+
if (RaftUtils.isLittleEndian()) return buf[bufPos] * 256 + buf[bufPos + 1];
|
|
189
|
+
return buf[bufPos + 1] * 256 + buf[bufPos];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
*
|
|
194
|
+
* Extract a big-endian uint8 from a uint8array
|
|
195
|
+
*
|
|
196
|
+
* @param buf - Uint8Array containing uint8
|
|
197
|
+
* @param pos - position (offset in buf) to get from
|
|
198
|
+
* @returns int16
|
|
199
|
+
*/
|
|
200
|
+
static getBEUint8FromBuf(buf: Uint8Array, pos: number): number {
|
|
201
|
+
return buf[pos];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
*
|
|
206
|
+
* Extract a big-endian int8 from a uint8array
|
|
207
|
+
*
|
|
208
|
+
* @param buf - Uint8Array containing int8
|
|
209
|
+
* @param pos - position (offset in buf) to get from
|
|
210
|
+
* @returns int16
|
|
211
|
+
*/
|
|
212
|
+
static getBEInt8FromBuf(buf: Uint8Array, pos: number): number {
|
|
213
|
+
return buf[pos] > 127 ? buf[pos] - 256 : buf[pos];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static isLittleEndian(): boolean {
|
|
217
|
+
// If already known just return
|
|
218
|
+
if (this._isEndianSet) return this._isLittleEndian;
|
|
219
|
+
|
|
220
|
+
// Figure out endian-ness
|
|
221
|
+
const a = new Uint32Array([0x12345678]);
|
|
222
|
+
const b = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
|
|
223
|
+
this._isEndianSet = true;
|
|
224
|
+
this._isLittleEndian = b[0] != 0x12;
|
|
225
|
+
return this._isLittleEndian;
|
|
226
|
+
// RaftUtils.debug("bigendian " + BigEndian);
|
|
227
|
+
|
|
228
|
+
// let buf = new ArrayBuffer(2);
|
|
229
|
+
// let shView = new Uint16Array(buf);
|
|
230
|
+
// buf[0] = 1;
|
|
231
|
+
// buf[1] = 0;
|
|
232
|
+
// RaftUtils.debug("Resulting short " + shView[0]);
|
|
233
|
+
// return shView[0] == 1;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// The following code from https://github.com/jsdom/abab
|
|
237
|
+
/**
|
|
238
|
+
* Implementation of atob() according to the HTML and Infra specs, except that
|
|
239
|
+
* instead of throwing INVALID_CHARACTER_ERR we return null.
|
|
240
|
+
*
|
|
241
|
+
* @param data - string containing base64 representation
|
|
242
|
+
* @returns Uint8Array
|
|
243
|
+
*
|
|
244
|
+
*/
|
|
245
|
+
static atob(data: string): Uint8Array | null {
|
|
246
|
+
// Web IDL requires DOMStrings to just be converted using ECMAScript
|
|
247
|
+
// ToString, which in our case amounts to using a template literal.
|
|
248
|
+
data = `${data}`;
|
|
249
|
+
// "Remove all ASCII whitespace from data."
|
|
250
|
+
data = data.replace(/[ \t\n\f\r]/g, "");
|
|
251
|
+
// "If data's length divides by 4 leaving no remainder, then: if data ends
|
|
252
|
+
// with one or two U+003D (=) code points, then remove them from data."
|
|
253
|
+
if (data.length % 4 === 0) {
|
|
254
|
+
data = data.replace(/==?$/, "");
|
|
255
|
+
}
|
|
256
|
+
// "If data's length divides by 4 leaving a remainder of 1, then return
|
|
257
|
+
// failure."
|
|
258
|
+
//
|
|
259
|
+
// "If data contains a code point that is not one of
|
|
260
|
+
//
|
|
261
|
+
// U+002B (+)
|
|
262
|
+
// U+002F (/)
|
|
263
|
+
// ASCII alphanumeric
|
|
264
|
+
//
|
|
265
|
+
// then return failure."
|
|
266
|
+
if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
// "Let output be an empty byte sequence."
|
|
270
|
+
const output = new Uint8Array((data.length * 3) / 4 + 1);
|
|
271
|
+
// "Let buffer be an empty buffer that can have bits appended to it."
|
|
272
|
+
//
|
|
273
|
+
// We append bits via left-shift and or. accumulatedBits is used to track
|
|
274
|
+
// when we've gotten to 24 bits.
|
|
275
|
+
let buffer = 0;
|
|
276
|
+
let accumulatedBits = 0;
|
|
277
|
+
// "Let position be a position variable for data, initially pointing at the
|
|
278
|
+
// start of data."
|
|
279
|
+
//
|
|
280
|
+
// "While position does not point past the end of data:"
|
|
281
|
+
let bytePos = 0;
|
|
282
|
+
for (let i = 0; i < data.length; i++) {
|
|
283
|
+
// "Find the code point pointed to by position in the second column of
|
|
284
|
+
// Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in
|
|
285
|
+
// the first cell of the same row.
|
|
286
|
+
//
|
|
287
|
+
// "Append to buffer the six bits corresponding to n, most significant bit
|
|
288
|
+
// first."
|
|
289
|
+
//
|
|
290
|
+
// atobLookup() implements the table from RFC 4648.
|
|
291
|
+
buffer <<= 6;
|
|
292
|
+
const atobVal = RaftUtils.atobLookup(data[i]);
|
|
293
|
+
if (atobVal !== undefined) {
|
|
294
|
+
buffer |= atobVal;
|
|
295
|
+
}
|
|
296
|
+
accumulatedBits += 6;
|
|
297
|
+
// "If buffer has accumulated 24 bits, interpret them as three 8-bit
|
|
298
|
+
// big-endian numbers. Append three bytes with values equal to those
|
|
299
|
+
// numbers to output, in the same order, and then empty buffer."
|
|
300
|
+
if (accumulatedBits === 24) {
|
|
301
|
+
output[bytePos++] = (buffer & 0xff0000) >> 16;
|
|
302
|
+
output[bytePos++] = (buffer & 0xff00) >> 8;
|
|
303
|
+
output[bytePos++] = buffer & 0xff;
|
|
304
|
+
buffer = accumulatedBits = 0;
|
|
305
|
+
}
|
|
306
|
+
// "Advance position by 1."
|
|
307
|
+
}
|
|
308
|
+
// "If buffer is not empty, it contains either 12 or 18 bits. If it contains
|
|
309
|
+
// 12 bits, then discard the last four and interpret the remaining eight as
|
|
310
|
+
// an 8-bit big-endian number. If it contains 18 bits, then discard the last
|
|
311
|
+
// two and interpret the remaining 16 as two 8-bit big-endian numbers. Append
|
|
312
|
+
// the one or two bytes with values equal to those one or two numbers to
|
|
313
|
+
// output, in the same order."
|
|
314
|
+
if (accumulatedBits === 12) {
|
|
315
|
+
buffer >>= 4;
|
|
316
|
+
output[bytePos++] = buffer & 0xff;
|
|
317
|
+
} else if (accumulatedBits === 18) {
|
|
318
|
+
buffer >>= 2;
|
|
319
|
+
output[bytePos++] = (buffer & 0xff00) >> 8;
|
|
320
|
+
output[bytePos++] = buffer & 0xff;
|
|
321
|
+
}
|
|
322
|
+
// "Return output."
|
|
323
|
+
return output.slice(0, bytePos);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* A lookup table for atob(), which converts an ASCII character to the
|
|
328
|
+
* corresponding six-bit number.
|
|
329
|
+
*/
|
|
330
|
+
static atobLookup(chr: string): number | undefined {
|
|
331
|
+
if (/[A-Z]/.test(chr)) {
|
|
332
|
+
return chr.charCodeAt(0) - "A".charCodeAt(0);
|
|
333
|
+
}
|
|
334
|
+
if (/[a-z]/.test(chr)) {
|
|
335
|
+
return chr.charCodeAt(0) - "a".charCodeAt(0) + 26;
|
|
336
|
+
}
|
|
337
|
+
if (/[0-9]/.test(chr)) {
|
|
338
|
+
return chr.charCodeAt(0) - "0".charCodeAt(0) + 52;
|
|
339
|
+
}
|
|
340
|
+
if (chr === "+") {
|
|
341
|
+
return 62;
|
|
342
|
+
}
|
|
343
|
+
if (chr === "/") {
|
|
344
|
+
return 63;
|
|
345
|
+
}
|
|
346
|
+
// Throw exception; should not be hit in tests
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* btoa() as defined by the HTML and Infra specs, which mostly just references
|
|
352
|
+
* RFC 4648.
|
|
353
|
+
*
|
|
354
|
+
* @param data - Uint8Array
|
|
355
|
+
* @returns string containing base64 representation
|
|
356
|
+
*
|
|
357
|
+
*/
|
|
358
|
+
static btoa(inBuf: Uint8Array): string {
|
|
359
|
+
let i;
|
|
360
|
+
// String conversion as required by Web IDL.
|
|
361
|
+
// s = `${s}`;
|
|
362
|
+
// "The btoa() method must throw an "InvalidCharacterError" DOMException if
|
|
363
|
+
// data contains any character whose code point is greater than U+00FF."
|
|
364
|
+
// for (i = 0; i < s.length; i++) {
|
|
365
|
+
// if (s.charCodeAt(i) > 255) {
|
|
366
|
+
// return null;
|
|
367
|
+
// }
|
|
368
|
+
// }
|
|
369
|
+
let out = "";
|
|
370
|
+
for (i = 0; i < inBuf.length; i += 3) {
|
|
371
|
+
const groupsOfSix: Array<number | undefined> = [
|
|
372
|
+
undefined,
|
|
373
|
+
undefined,
|
|
374
|
+
undefined,
|
|
375
|
+
undefined,
|
|
376
|
+
];
|
|
377
|
+
groupsOfSix[0] = inBuf[i] >> 2;
|
|
378
|
+
groupsOfSix[1] = (inBuf[i] & 0x03) << 4;
|
|
379
|
+
if (inBuf.length > i + 1) {
|
|
380
|
+
groupsOfSix[1] |= inBuf[i + 1] >> 4;
|
|
381
|
+
groupsOfSix[2] = (inBuf[i + 1] & 0x0f) << 2;
|
|
382
|
+
if (inBuf.length > i + 2) {
|
|
383
|
+
groupsOfSix[2] |= inBuf[i + 2] >> 6;
|
|
384
|
+
groupsOfSix[3] = inBuf[i + 2] & 0x3f;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
for (let j = 0; j < groupsOfSix.length; j++) {
|
|
388
|
+
if (typeof groupsOfSix[j] === "undefined") {
|
|
389
|
+
out += "=";
|
|
390
|
+
} else {
|
|
391
|
+
out += this.btoaLookup(groupsOfSix[j]);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Lookup table for btoa(), which converts a six-bit number into the
|
|
400
|
+
* corresponding ASCII character.
|
|
401
|
+
*/
|
|
402
|
+
static btoaLookup(idx: number | undefined): string | undefined {
|
|
403
|
+
if (idx === undefined) {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
if (idx < 26) {
|
|
407
|
+
return String.fromCharCode(idx + "A".charCodeAt(0));
|
|
408
|
+
}
|
|
409
|
+
if (idx < 52) {
|
|
410
|
+
return String.fromCharCode(idx - 26 + "a".charCodeAt(0));
|
|
411
|
+
}
|
|
412
|
+
if (idx < 62) {
|
|
413
|
+
return String.fromCharCode(idx - 52 + "0".charCodeAt(0));
|
|
414
|
+
}
|
|
415
|
+
if (idx === 62) {
|
|
416
|
+
return "+";
|
|
417
|
+
}
|
|
418
|
+
if (idx === 63) {
|
|
419
|
+
return "/";
|
|
420
|
+
}
|
|
421
|
+
// Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests.
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static buf2hex(buffer: Uint8Array) {
|
|
426
|
+
return Array.prototype.map
|
|
427
|
+
.call(buffer, (x: number) => ("00" + x.toString(16)).slice(-2))
|
|
428
|
+
.join("");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
static padStartFn(
|
|
432
|
+
inStr: string,
|
|
433
|
+
targetLength: number,
|
|
434
|
+
padString: string
|
|
435
|
+
): string {
|
|
436
|
+
targetLength = targetLength >> 0; //truncate if number or convert non-number to 0;
|
|
437
|
+
padString = String(typeof padString !== "undefined" ? padString : " ");
|
|
438
|
+
if (inStr.length > targetLength) {
|
|
439
|
+
return String(inStr);
|
|
440
|
+
} else {
|
|
441
|
+
targetLength = targetLength - inStr.length;
|
|
442
|
+
if (targetLength > padString.length) {
|
|
443
|
+
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
|
444
|
+
}
|
|
445
|
+
return padString.slice(0, targetLength) + String(inStr);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
static isVersionGreater(v1: string, v2: string) {
|
|
450
|
+
try {
|
|
451
|
+
return semverGt(v1, v2);
|
|
452
|
+
} catch (e) {
|
|
453
|
+
// one of the two versions is invalid, return true
|
|
454
|
+
RaftLog.error(`isVersionGreater - invalid version ${v1} or ${v2} ${e}`);
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
static isVersionEqual(v1: string, v2: string) {
|
|
460
|
+
try {
|
|
461
|
+
return semverEq(v1, v2);
|
|
462
|
+
} catch (e) {
|
|
463
|
+
// one of the two versions is invalid, return false
|
|
464
|
+
RaftLog.error(`isVersionEqual - invalid version ${v1} or ${v2} ${e}`);
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static withTimeout(ms: number, promise: Promise<any>) {
|
|
470
|
+
let id: NodeJS.Timeout;
|
|
471
|
+
const timeout = new Promise((_, reject) => {
|
|
472
|
+
id = setTimeout(() => {
|
|
473
|
+
clearTimeout(id);
|
|
474
|
+
reject('Timed out in '+ ms + 'ms.')
|
|
475
|
+
}, ms)
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
return Promise.race([
|
|
479
|
+
promise,
|
|
480
|
+
timeout
|
|
481
|
+
])
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// RaftWifiTypes
|
|
4
|
+
// Part of RaftJS
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson & Chris Greening 2020-2024
|
|
7
|
+
// (C) 2020-2024 All rights reserved
|
|
8
|
+
//
|
|
9
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
|
|
11
|
+
export enum RaftWifiConnState {
|
|
12
|
+
WIFI_CONN_NONE,
|
|
13
|
+
WIFI_CONN_CONNECTED,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class RaftWifiConnStatus {
|
|
17
|
+
connState = RaftWifiConnState.WIFI_CONN_NONE;
|
|
18
|
+
isPaused = false;
|
|
19
|
+
ipAddress = '';
|
|
20
|
+
hostname = '';
|
|
21
|
+
ssid = '';
|
|
22
|
+
bssid = '';
|
|
23
|
+
validMs = 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class RaftSysModInfoWiFi {
|
|
27
|
+
rslt = 'ok';
|
|
28
|
+
isConn = 0;
|
|
29
|
+
isPaused = 0;
|
|
30
|
+
connState = 'None';
|
|
31
|
+
SSID = '';
|
|
32
|
+
IP = '';
|
|
33
|
+
Hostname = '';
|
|
34
|
+
WiFiMAC = '';
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// TestDataGen
|
|
4
|
+
// Test data generation for Raft device manager
|
|
5
|
+
//
|
|
6
|
+
// Rob Dobson (C) 2024
|
|
7
|
+
//
|
|
8
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
9
|
+
|
|
10
|
+
class TestDataGen {
|
|
11
|
+
|
|
12
|
+
// Start generating test data and use the callback provided to handle the data JSON string
|
|
13
|
+
public start(handleDeviceMsgJson: (msg: string) => void) {
|
|
14
|
+
// Start timer for testing which sends randomized data
|
|
15
|
+
let psVals = [50, 150];
|
|
16
|
+
let iterCount = 0;
|
|
17
|
+
let psValAltCount = 0;
|
|
18
|
+
let alsVal = 1000;
|
|
19
|
+
let alsValInc = 1;
|
|
20
|
+
let whiteVal = 1000;
|
|
21
|
+
let whiteValInc = 10;
|
|
22
|
+
let x = 0;
|
|
23
|
+
let y = 0;
|
|
24
|
+
let z = 0;
|
|
25
|
+
setInterval(() => {
|
|
26
|
+
// Performance testing
|
|
27
|
+
// const debugPerfTimerStart = performance.now();
|
|
28
|
+
|
|
29
|
+
// Vars for VSNL4040
|
|
30
|
+
const psVar = psVals[psValAltCount] + Math.floor(Math.random() * 10) - 5;
|
|
31
|
+
const alsVar = alsVal;
|
|
32
|
+
const whiteVar = whiteVal;
|
|
33
|
+
iterCount++;
|
|
34
|
+
if (iterCount % 10 === 0) {
|
|
35
|
+
psValAltCount = (psValAltCount + 1) % 2;
|
|
36
|
+
}
|
|
37
|
+
alsVal += alsValInc + Math.floor(Math.random() * 10) - 5;
|
|
38
|
+
if (iterCount % 1000 === 0) {
|
|
39
|
+
alsValInc = -alsValInc;
|
|
40
|
+
}
|
|
41
|
+
whiteVal += whiteValInc + Math.floor(Math.random() * 10) - 5;
|
|
42
|
+
if (iterCount % 100 === 0) {
|
|
43
|
+
whiteValInc = -whiteValInc;
|
|
44
|
+
}
|
|
45
|
+
const tsHexHighLow = ((Date.now()) & 0xffff).toString(16).padStart(4, '0');
|
|
46
|
+
const tsHexNextHighLow = ((Date.now()+1) & 0xffff).toString(16).padStart(4, '0');
|
|
47
|
+
const psHexLowHigh = ((psVar & 0xff) << 8 | (psVar >> 8)).toString(16).padStart(4, '0');
|
|
48
|
+
const alsHexLowHigh = ((alsVar & 0xff) << 8 | (alsVar >> 8)).toString(16).padStart(4, '0');
|
|
49
|
+
const whiteHexLowHigh = ((whiteVar & 0xff) << 8 | (whiteVar >> 8)).toString(16).padStart(4, '0');
|
|
50
|
+
|
|
51
|
+
// Vars for ADXL313
|
|
52
|
+
x += Math.floor(Math.random() * 10) - 5;
|
|
53
|
+
y += Math.floor(Math.random() * 10) - 5;
|
|
54
|
+
z += Math.floor(Math.random() * 10) - 5;
|
|
55
|
+
// Ensure x,y,z in range -512 to 511
|
|
56
|
+
x = Math.floor(Math.min(511, Math.max(-512, x)));
|
|
57
|
+
y = Math.floor(Math.min(511, Math.max(-512, y)));
|
|
58
|
+
z = Math.floor(Math.min(511, Math.max(-512, z)));
|
|
59
|
+
// Convert x,y,z to 10-bit signed values
|
|
60
|
+
x = x & 0x3ff;
|
|
61
|
+
y = y & 0x3ff;
|
|
62
|
+
z = z & 0x3ff;
|
|
63
|
+
const xHexLowHigh = ((x & 0xff) << 8 | (x >> 8)).toString(16).padStart(4, '0');
|
|
64
|
+
const yHexLowHigh = ((y & 0xff) << 8 | (y >> 8)).toString(16).padStart(4, '0');
|
|
65
|
+
const zHexLowHigh = ((z & 0xff) << 8 | (z >> 8)).toString(16).padStart(4, '0');
|
|
66
|
+
|
|
67
|
+
interface DevMsg {
|
|
68
|
+
x: string;
|
|
69
|
+
_t: string;
|
|
70
|
+
_o: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface DevMsgs {
|
|
74
|
+
[address: string]: DevMsg;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Dev messages
|
|
78
|
+
let devMsgs: DevMsgs = {
|
|
79
|
+
"0x60@1": {
|
|
80
|
+
x: `${tsHexHighLow}${psHexLowHigh}${alsHexLowHigh}${whiteHexLowHigh}`,
|
|
81
|
+
_t: "VCNL4040",
|
|
82
|
+
_o: this.onlineFrom(iterCount, 0, 100)
|
|
83
|
+
},
|
|
84
|
+
"0x38@1": {
|
|
85
|
+
x: `${tsHexHighLow}${xHexLowHigh}${yHexLowHigh}${zHexLowHigh}`,
|
|
86
|
+
_t: "ADXL313",
|
|
87
|
+
_o: this.onlineFrom(iterCount, 0, 150)
|
|
88
|
+
},
|
|
89
|
+
"0x6f@41": {
|
|
90
|
+
x: `${tsHexHighLow}${this.toHex(this.randInt(0,1)*4,1)}`,
|
|
91
|
+
_t: "QwiicButton",
|
|
92
|
+
_o: this.onlineFrom(iterCount, 0, 120)
|
|
93
|
+
},
|
|
94
|
+
"0x6f@42": {
|
|
95
|
+
x: `${tsHexHighLow}${this.toHex(this.randInt(0,1)*4,1)}`,
|
|
96
|
+
_t: "QwiicButton",
|
|
97
|
+
_o: this.onlineFrom(iterCount, 0, 90)
|
|
98
|
+
},
|
|
99
|
+
"0x23@0": {
|
|
100
|
+
x: "",
|
|
101
|
+
_t: "QwiicLEDStick",
|
|
102
|
+
_o: this.onlineFrom(iterCount, 100, 180)
|
|
103
|
+
},
|
|
104
|
+
"0x28@0": {
|
|
105
|
+
x: `${tsHexHighLow}${this.toHex(this.randInt(0,7),2)}${tsHexNextHighLow}${this.toHex(this.randInt(0,7),2)}`,
|
|
106
|
+
_t: "CAP1203",
|
|
107
|
+
_o: true
|
|
108
|
+
},
|
|
109
|
+
"0x57@0": {
|
|
110
|
+
x: `${tsHexHighLow}${this.toHex(5,1)}${this.toHex(0x00,1)}${this.toHex(0x04,1)}${this.toHex(Math.floor(10000*Math.sin(iterCount/10.0))+100000,3)}${this.toHex(Math.floor(10000*Math.cos(iterCount/10.0))+100000,3)}${this.toHex(0,42)}`,
|
|
111
|
+
_t: "MAX30101",
|
|
112
|
+
_o: true
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
interface DevMsgsEnabled {
|
|
117
|
+
[key: string]: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// const devMsgsEnabled:DevMsgsEnabled = { "0x6f@42":true };
|
|
121
|
+
const devMsgsEnabled:DevMsgsEnabled = {};
|
|
122
|
+
const I2CA: DevMsgs = Object.keys(devMsgsEnabled).length === 0 ? devMsgs : {};
|
|
123
|
+
for (const key in devMsgs) {
|
|
124
|
+
if (devMsgsEnabled[key]) {
|
|
125
|
+
I2CA[key] = devMsgs[key];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const msg = JSON.stringify({I2CA: I2CA});
|
|
130
|
+
|
|
131
|
+
// Performance testing
|
|
132
|
+
// const debugPerfTimerEnd = performance.now();
|
|
133
|
+
|
|
134
|
+
// Call the callback to handle the data JSON string
|
|
135
|
+
// const debugHandleMsgStart = performance.now();
|
|
136
|
+
handleDeviceMsgJson(msg);
|
|
137
|
+
// const debugHandleMsgEnd = performance.now();
|
|
138
|
+
|
|
139
|
+
// console.log(`iterCount ${iterCount} genTestDataTime ${debugPerfTimerEnd - debugPerfTimerStart} handleTestDataTime ${debugHandleMsgEnd - debugHandleMsgStart}`);
|
|
140
|
+
|
|
141
|
+
// console.log(`iterCount ${iterCount} x ${x} Test message sent: ${JSON.stringify(JSON.parse(msg))}`);
|
|
142
|
+
}, 200);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private toHex(val: number, numBytes: number) {
|
|
146
|
+
return val.toString(16).padStart(numBytes * 2, '0');
|
|
147
|
+
}
|
|
148
|
+
private randInt(min: number, max: number) {
|
|
149
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
150
|
+
}
|
|
151
|
+
private onlineFrom(iterCount: number, start: number, end: number) {
|
|
152
|
+
const ic = iterCount % 200;
|
|
153
|
+
return ic >= start && ic <= end;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default TestDataGen;
|