brilliantsole 0.0.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/LICENSE +21 -0
- package/README.md +21 -0
- package/build/brilliantsole.cjs +5957 -0
- package/build/brilliantsole.cjs.map +1 -0
- package/build/brilliantsole.js +5448 -0
- package/build/brilliantsole.js.map +1 -0
- package/build/brilliantsole.ls.js +4872 -0
- package/build/brilliantsole.ls.js.map +1 -0
- package/build/brilliantsole.min.js +6 -0
- package/build/brilliantsole.min.js.map +1 -0
- package/build/brilliantsole.module.d.ts +908 -0
- package/build/brilliantsole.module.js +5412 -0
- package/build/brilliantsole.module.js.map +1 -0
- package/build/brilliantsole.module.min.d.ts +908 -0
- package/build/brilliantsole.module.min.js +6 -0
- package/build/brilliantsole.module.min.js.map +1 -0
- package/build/brilliantsole.node.module.d.ts +908 -0
- package/build/brilliantsole.node.module.js +5906 -0
- package/build/brilliantsole.node.module.js.map +1 -0
- package/build/dts/BS.d.ts +25 -0
- package/build/dts/Device.d.ts +136 -0
- package/build/dts/DeviceInformationManager.d.ts +56 -0
- package/build/dts/DeviceManager.d.ts +67 -0
- package/build/dts/FileTransferManager.d.ts +84 -0
- package/build/dts/FirmwareManager.d.ts +71 -0
- package/build/dts/InformationManager.d.ts +66 -0
- package/build/dts/TfliteManager.d.ts +92 -0
- package/build/dts/connection/BaseConnectionManager.d.ts +59 -0
- package/build/dts/connection/ClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/WebSocketClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +10 -0
- package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +42 -0
- package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +20 -0
- package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +14 -0
- package/build/dts/connection/webSocket/ClientConnectionManager.d.ts +23 -0
- package/build/dts/connection/webSocket/WebSocketClientConnectionManager.d.ts +23 -0
- package/build/dts/devicePair/DevicePair.d.ts +60 -0
- package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +25 -0
- package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +33 -0
- package/build/dts/scanner/BaseScanner.d.ts +66 -0
- package/build/dts/scanner/NobleScanner.d.ts +17 -0
- package/build/dts/scanner/Scanner.d.ts +3 -0
- package/build/dts/sensor/BarometerSensorDataManager.d.ts +16 -0
- package/build/dts/sensor/MotionSensorDataManager.d.ts +69 -0
- package/build/dts/sensor/PressureSensorDataManager.d.ts +36 -0
- package/build/dts/sensor/SensorConfigurationManager.d.ts +44 -0
- package/build/dts/sensor/SensorDataManager.d.ts +40 -0
- package/build/dts/server/BaseClient.d.ts +85 -0
- package/build/dts/server/BaseServer.d.ts +48 -0
- package/build/dts/server/ServerUtils.d.ts +23 -0
- package/build/dts/server/udp/UDPServer.d.ts +11 -0
- package/build/dts/server/udp/UDPUtils.d.ts +9 -0
- package/build/dts/server/websocket/WebSocketClient.d.ts +17 -0
- package/build/dts/server/websocket/WebSocketServer.d.ts +13 -0
- package/build/dts/server/websocket/WebSocketUtils.d.ts +9 -0
- package/build/dts/utils/ArrayBufferUtils.d.ts +7 -0
- package/build/dts/utils/ArrayUtils.d.ts +2 -0
- package/build/dts/utils/CenterOfPressureHelper.d.ts +15 -0
- package/build/dts/utils/Console.d.ts +34 -0
- package/build/dts/utils/EventDispatcher.d.ts +50 -0
- package/build/dts/utils/EventUtils.d.ts +6 -0
- package/build/dts/utils/MathUtils.d.ts +21 -0
- package/build/dts/utils/ParseUtils.d.ts +5 -0
- package/build/dts/utils/RangeHelper.d.ts +8 -0
- package/build/dts/utils/Text.d.ts +6 -0
- package/build/dts/utils/Timer.d.ts +14 -0
- package/build/dts/utils/TypeScriptUtils.d.ts +19 -0
- package/build/dts/utils/cbor.d.ts +6 -0
- package/build/dts/utils/checksum.d.ts +3 -0
- package/build/dts/utils/environment.d.ts +13 -0
- package/build/dts/utils/mcumgr.d.ts +88 -0
- package/build/dts/utils/stringUtils.d.ts +2 -0
- package/build/dts/vibration/VibrationManager.d.ts +45 -0
- package/build/dts/vibration/VibrationWaveformEffects.d.ts +2 -0
- package/build/index.d.ts +908 -0
- package/build/index.node.d.ts +908 -0
- package/examples/3d/index.html +109 -0
- package/examples/3d/scene.html +57 -0
- package/examples/3d/script.js +419 -0
- package/examples/balance/index.html +138 -0
- package/examples/balance/script.js +243 -0
- package/examples/basic/index.html +327 -0
- package/examples/basic/script.js +1093 -0
- package/examples/center-of-pressure/index.html +132 -0
- package/examples/center-of-pressure/script.js +207 -0
- package/examples/device-pair/index.html +72 -0
- package/examples/device-pair/script.js +187 -0
- package/examples/edge-impulse/index.html +94 -0
- package/examples/edge-impulse/script.js +1033 -0
- package/examples/graph/index.html +83 -0
- package/examples/graph/script.js +469 -0
- package/examples/machine-learning/index.html +366 -0
- package/examples/machine-learning/script.js +1774 -0
- package/examples/pressure/index.html +145 -0
- package/examples/pressure/script.js +201 -0
- package/examples/recording/index.html +187 -0
- package/examples/recording/script.js +736 -0
- package/examples/server/index.html +266 -0
- package/examples/server/script.js +925 -0
- package/examples/utils/aframe/fingertip-button-component.js +201 -0
- package/examples/utils/aframe/fingertip-collider-target-component.js +102 -0
- package/examples/utils/aframe/fingertip-colliders-component.js +147 -0
- package/examples/utils/three/three.module.min.js +24846 -0
- package/examples/webxr/index.html +221 -0
- package/examples/webxr/script.js +1127 -0
- package/package.json +83 -0
- package/src/BS.ts +68 -0
- package/src/Device.ts +734 -0
- package/src/DeviceInformationManager.ts +146 -0
- package/src/DeviceManager.ts +354 -0
- package/src/FileTransferManager.ts +452 -0
- package/src/FirmwareManager.ts +357 -0
- package/src/InformationManager.ts +283 -0
- package/src/TfliteManager.ts +450 -0
- package/src/connection/BaseConnectionManager.ts +255 -0
- package/src/connection/ClientConnectionManager.ts +120 -0
- package/src/connection/bluetooth/BluetoothConnectionManager.ts +34 -0
- package/src/connection/bluetooth/NobleConnectionManager.ts +302 -0
- package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +269 -0
- package/src/connection/bluetooth/bluetoothUUIDs.ts +218 -0
- package/src/devicePair/DevicePair.ts +253 -0
- package/src/devicePair/DevicePairPressureSensorDataManager.ts +82 -0
- package/src/devicePair/DevicePairSensorDataManager.ts +90 -0
- package/src/scanner/BaseScanner.ts +189 -0
- package/src/scanner/NobleScanner.ts +195 -0
- package/src/scanner/Scanner.ts +16 -0
- package/src/sensor/BarometerSensorDataManager.ts +41 -0
- package/src/sensor/MotionSensorDataManager.ts +151 -0
- package/src/sensor/PressureSensorDataManager.ts +112 -0
- package/src/sensor/SensorConfigurationManager.ts +177 -0
- package/src/sensor/SensorDataManager.ts +166 -0
- package/src/server/BaseClient.ts +368 -0
- package/src/server/BaseServer.ts +344 -0
- package/src/server/ServerUtils.ts +93 -0
- package/src/server/udp/UDPServer.ts +229 -0
- package/src/server/udp/UDPUtils.ts +20 -0
- package/src/server/websocket/WebSocketClient.ts +179 -0
- package/src/server/websocket/WebSocketServer.ts +184 -0
- package/src/server/websocket/WebSocketUtils.ts +20 -0
- package/src/utils/ArrayBufferUtils.ts +88 -0
- package/src/utils/ArrayUtils.ts +15 -0
- package/src/utils/CenterOfPressureHelper.ts +39 -0
- package/src/utils/Console.ts +156 -0
- package/src/utils/EventDispatcher.ts +153 -0
- package/src/utils/EventUtils.ts +41 -0
- package/src/utils/MathUtils.ts +53 -0
- package/src/utils/ParseUtils.ts +46 -0
- package/src/utils/RangeHelper.ts +38 -0
- package/src/utils/Text.ts +30 -0
- package/src/utils/Timer.ts +72 -0
- package/src/utils/TypeScriptUtils.ts +22 -0
- package/src/utils/cbor.js +429 -0
- package/src/utils/checksum.ts +41 -0
- package/src/utils/environment.ts +46 -0
- package/src/utils/mcumgr.js +444 -0
- package/src/utils/stringUtils.ts +11 -0
- package/src/vibration/VibrationManager.ts +308 -0
- package/src/vibration/VibrationWaveformEffects.ts +128 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import Device, { SendSmpMessageCallback } from "./Device.ts";
|
|
2
|
+
import { getFileBuffer } from "./utils/ArrayBufferUtils.ts";
|
|
3
|
+
import { createConsole } from "./utils/Console.ts";
|
|
4
|
+
import EventDispatcher from "./utils/EventDispatcher.ts";
|
|
5
|
+
import { MCUManager, constants } from "./utils/mcumgr.js";
|
|
6
|
+
import { FileLike } from "./utils/ArrayBufferUtils.ts";
|
|
7
|
+
import autoBind from "auto-bind";
|
|
8
|
+
|
|
9
|
+
const _console = createConsole("FirmwareManager", { log: true });
|
|
10
|
+
|
|
11
|
+
export const FirmwareMessageTypes = ["smp"] as const;
|
|
12
|
+
export type FirmwareMessageType = (typeof FirmwareMessageTypes)[number];
|
|
13
|
+
|
|
14
|
+
export const FirmwareEventTypes = [
|
|
15
|
+
...FirmwareMessageTypes,
|
|
16
|
+
"firmwareImages",
|
|
17
|
+
"firmwareUploadProgress",
|
|
18
|
+
"firmwareStatus",
|
|
19
|
+
"firmwareUploadComplete",
|
|
20
|
+
] as const;
|
|
21
|
+
export type FirmwareEventType = (typeof FirmwareEventTypes)[number];
|
|
22
|
+
|
|
23
|
+
export const FirmwareStatuses = ["idle", "uploading", "uploaded", "pending", "testing", "erasing"] as const;
|
|
24
|
+
export type FirmwareStatus = (typeof FirmwareStatuses)[number];
|
|
25
|
+
|
|
26
|
+
export interface FirmwareImage {
|
|
27
|
+
slot: number;
|
|
28
|
+
active: boolean;
|
|
29
|
+
confirmed: boolean;
|
|
30
|
+
pending: boolean;
|
|
31
|
+
permanent: boolean;
|
|
32
|
+
bootable: boolean;
|
|
33
|
+
version: string;
|
|
34
|
+
hash?: Uint8Array;
|
|
35
|
+
empty?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface FirmwareEventMessages {
|
|
39
|
+
smp: { dataView: DataView };
|
|
40
|
+
firmwareImages: { firmwareImages: FirmwareImage[] };
|
|
41
|
+
firmwareUploadProgress: { progress: number };
|
|
42
|
+
firmwareStatus: { firmwareStatus: FirmwareStatus };
|
|
43
|
+
//firmwareUploadComplete: {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type FirmwareEventDispatcher = EventDispatcher<Device, FirmwareEventType, FirmwareEventMessages>;
|
|
47
|
+
|
|
48
|
+
class FirmwareManager {
|
|
49
|
+
sendMessage!: SendSmpMessageCallback;
|
|
50
|
+
|
|
51
|
+
constructor() {
|
|
52
|
+
this.#assignMcuManagerCallbacks();
|
|
53
|
+
autoBind(this);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
eventDispatcher!: FirmwareEventDispatcher;
|
|
57
|
+
get addEventListenter() {
|
|
58
|
+
return this.eventDispatcher.addEventListener;
|
|
59
|
+
}
|
|
60
|
+
get #dispatchEvent() {
|
|
61
|
+
return this.eventDispatcher.dispatchEvent;
|
|
62
|
+
}
|
|
63
|
+
get removeEventListener() {
|
|
64
|
+
return this.eventDispatcher.removeEventListener;
|
|
65
|
+
}
|
|
66
|
+
get waitForEvent() {
|
|
67
|
+
return this.eventDispatcher.waitForEvent;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
parseMessage(messageType: FirmwareMessageType, dataView: DataView) {
|
|
71
|
+
_console.log({ messageType });
|
|
72
|
+
|
|
73
|
+
switch (messageType) {
|
|
74
|
+
case "smp":
|
|
75
|
+
this.#mcuManager._notification(Array.from(new Uint8Array(dataView.buffer)));
|
|
76
|
+
this.#dispatchEvent("smp", { dataView });
|
|
77
|
+
break;
|
|
78
|
+
default:
|
|
79
|
+
throw Error(`uncaught messageType ${messageType}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async uploadFirmware(file: FileLike) {
|
|
84
|
+
_console.log("uploadFirmware", file);
|
|
85
|
+
|
|
86
|
+
const promise = this.waitForEvent("firmwareUploadComplete");
|
|
87
|
+
|
|
88
|
+
await this.getImages();
|
|
89
|
+
|
|
90
|
+
const arrayBuffer = await getFileBuffer(file);
|
|
91
|
+
const imageInfo = await this.#mcuManager.imageInfo(arrayBuffer);
|
|
92
|
+
_console.log({ imageInfo });
|
|
93
|
+
|
|
94
|
+
this.#mcuManager.cmdUpload(arrayBuffer, 1);
|
|
95
|
+
|
|
96
|
+
this.#updateStatus("uploading");
|
|
97
|
+
|
|
98
|
+
await promise;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#status: FirmwareStatus = "idle";
|
|
102
|
+
get status() {
|
|
103
|
+
return this.#status;
|
|
104
|
+
}
|
|
105
|
+
#updateStatus(newStatus: FirmwareStatus) {
|
|
106
|
+
_console.assertEnumWithError(newStatus, FirmwareStatuses);
|
|
107
|
+
if (this.#status == newStatus) {
|
|
108
|
+
_console.log(`redundant firmwareStatus assignment "${newStatus}"`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.#status = newStatus;
|
|
113
|
+
_console.log({ firmwareStatus: this.#status });
|
|
114
|
+
this.#dispatchEvent("firmwareStatus", { firmwareStatus: this.#status });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// COMMANDS
|
|
118
|
+
|
|
119
|
+
#images!: FirmwareImage[];
|
|
120
|
+
get images() {
|
|
121
|
+
return this.#images;
|
|
122
|
+
}
|
|
123
|
+
#assertImages() {
|
|
124
|
+
_console.assertWithError(this.#images, "didn't get imageState");
|
|
125
|
+
}
|
|
126
|
+
#assertValidImageIndex(imageIndex: number) {
|
|
127
|
+
_console.assertTypeWithError(imageIndex, "number");
|
|
128
|
+
_console.assertWithError(imageIndex == 0 || imageIndex == 1, "imageIndex must be 0 or 1");
|
|
129
|
+
}
|
|
130
|
+
async getImages() {
|
|
131
|
+
const promise = this.waitForEvent("firmwareImages");
|
|
132
|
+
|
|
133
|
+
_console.log("getting firmware image state...");
|
|
134
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.cmdImageState()).buffer);
|
|
135
|
+
|
|
136
|
+
await promise;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async testImage(imageIndex: number = 1) {
|
|
140
|
+
this.#assertValidImageIndex(imageIndex);
|
|
141
|
+
this.#assertImages();
|
|
142
|
+
if (!this.#images[imageIndex]) {
|
|
143
|
+
_console.log(`image ${imageIndex} not found`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (this.#images[imageIndex].pending == true) {
|
|
147
|
+
_console.log(`image ${imageIndex} is already pending`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (this.#images[imageIndex].empty) {
|
|
151
|
+
_console.log(`image ${imageIndex} is empty`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const promise = this.waitForEvent("smp");
|
|
156
|
+
|
|
157
|
+
_console.log("testing firmware image...");
|
|
158
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.cmdImageTest(this.#images[imageIndex].hash)).buffer);
|
|
159
|
+
|
|
160
|
+
await promise;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async eraseImage() {
|
|
164
|
+
this.#assertImages();
|
|
165
|
+
const promise = this.waitForEvent("smp");
|
|
166
|
+
|
|
167
|
+
_console.log("erasing image...");
|
|
168
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.cmdImageErase()).buffer);
|
|
169
|
+
|
|
170
|
+
this.#updateStatus("erasing");
|
|
171
|
+
|
|
172
|
+
await promise;
|
|
173
|
+
await this.getImages();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async confirmImage(imageIndex: number = 0) {
|
|
177
|
+
this.#assertValidImageIndex(imageIndex);
|
|
178
|
+
this.#assertImages();
|
|
179
|
+
if (this.#images[imageIndex].confirmed === true) {
|
|
180
|
+
_console.log(`image ${imageIndex} is already confirmed`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const promise = this.waitForEvent("smp");
|
|
185
|
+
|
|
186
|
+
_console.log("confirming image...");
|
|
187
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.cmdImageConfirm(this.#images[imageIndex].hash)).buffer);
|
|
188
|
+
|
|
189
|
+
await promise;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async echo(string: string) {
|
|
193
|
+
_console.assertTypeWithError(string, "string");
|
|
194
|
+
|
|
195
|
+
const promise = this.waitForEvent("smp");
|
|
196
|
+
|
|
197
|
+
_console.log("sending echo...");
|
|
198
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.smpEcho(string)).buffer);
|
|
199
|
+
|
|
200
|
+
await promise;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async reset() {
|
|
204
|
+
const promise = this.waitForEvent("smp");
|
|
205
|
+
|
|
206
|
+
_console.log("resetting...");
|
|
207
|
+
this.sendMessage(Uint8Array.from(this.#mcuManager.cmdReset()).buffer);
|
|
208
|
+
|
|
209
|
+
await promise;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// MTU
|
|
213
|
+
#mtu!: number;
|
|
214
|
+
get mtu() {
|
|
215
|
+
return this.#mtu;
|
|
216
|
+
}
|
|
217
|
+
set mtu(newMtu: number) {
|
|
218
|
+
this.#mtu = newMtu;
|
|
219
|
+
this.#mcuManager._mtu = newMtu;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// MCUManager
|
|
223
|
+
#mcuManager = new MCUManager();
|
|
224
|
+
|
|
225
|
+
#assignMcuManagerCallbacks() {
|
|
226
|
+
this.#mcuManager.onMessage(this.#onMcuMessage.bind(this));
|
|
227
|
+
|
|
228
|
+
this.#mcuManager.onFileDownloadNext(this.#onMcuFileDownloadNext);
|
|
229
|
+
this.#mcuManager.onFileDownloadProgress(this.#onMcuFileDownloadProgress.bind(this));
|
|
230
|
+
this.#mcuManager.onFileDownloadFinished(this.#onMcuFileDownloadFinished.bind(this));
|
|
231
|
+
|
|
232
|
+
this.#mcuManager.onFileUploadNext(this.#onMcuFileUploadNext.bind(this));
|
|
233
|
+
this.#mcuManager.onFileUploadProgress(this.#onMcuFileUploadProgress.bind(this));
|
|
234
|
+
this.#mcuManager.onFileUploadFinished(this.#onMcuFileUploadFinished.bind(this));
|
|
235
|
+
|
|
236
|
+
this.#mcuManager.onImageUploadNext(this.#onMcuImageUploadNext.bind(this));
|
|
237
|
+
this.#mcuManager.onImageUploadProgress(this.#onMcuImageUploadProgress.bind(this));
|
|
238
|
+
this.#mcuManager.onImageUploadFinished(this.#onMcuImageUploadFinished.bind(this));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#onMcuMessage({ op, group, id, data, length }: { op: number; group: number; id: number; data: any; length: number }) {
|
|
242
|
+
_console.log("onMcuMessage", ...arguments);
|
|
243
|
+
|
|
244
|
+
switch (group) {
|
|
245
|
+
case constants.MGMT_GROUP_ID_OS:
|
|
246
|
+
switch (id) {
|
|
247
|
+
case constants.OS_MGMT_ID_ECHO:
|
|
248
|
+
_console.log(`echo "${data.r}"`);
|
|
249
|
+
break;
|
|
250
|
+
case constants.OS_MGMT_ID_TASKSTAT:
|
|
251
|
+
_console.table(data.tasks);
|
|
252
|
+
break;
|
|
253
|
+
case constants.OS_MGMT_ID_MPSTAT:
|
|
254
|
+
_console.log(data);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case constants.MGMT_GROUP_ID_IMAGE:
|
|
259
|
+
switch (id) {
|
|
260
|
+
case constants.IMG_MGMT_ID_STATE:
|
|
261
|
+
this.#onMcuImageState(data);
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
default:
|
|
265
|
+
throw Error(`uncaught mcuMessage group ${group}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#onMcuFileDownloadNext() {
|
|
270
|
+
_console.log("onMcuFileDownloadNext", ...arguments);
|
|
271
|
+
}
|
|
272
|
+
#onMcuFileDownloadProgress() {
|
|
273
|
+
_console.log("onMcuFileDownloadProgress", ...arguments);
|
|
274
|
+
}
|
|
275
|
+
#onMcuFileDownloadFinished() {
|
|
276
|
+
_console.log("onMcuFileDownloadFinished", ...arguments);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#onMcuFileUploadNext() {
|
|
280
|
+
_console.log("onMcuFileUploadNext");
|
|
281
|
+
}
|
|
282
|
+
#onMcuFileUploadProgress() {
|
|
283
|
+
_console.log("onMcuFileUploadProgress");
|
|
284
|
+
}
|
|
285
|
+
#onMcuFileUploadFinished() {
|
|
286
|
+
_console.log("onMcuFileUploadFinished");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#onMcuImageUploadNext({ packet }: { packet: number[] }) {
|
|
290
|
+
_console.log("onMcuImageUploadNext");
|
|
291
|
+
this.sendMessage(Uint8Array.from(packet).buffer);
|
|
292
|
+
}
|
|
293
|
+
#onMcuImageUploadProgress({ percentage }: { percentage: number }) {
|
|
294
|
+
const progress = percentage / 100;
|
|
295
|
+
_console.log("onMcuImageUploadProgress", ...arguments);
|
|
296
|
+
this.#dispatchEvent("firmwareUploadProgress", { progress });
|
|
297
|
+
}
|
|
298
|
+
async #onMcuImageUploadFinished() {
|
|
299
|
+
_console.log("onMcuImageUploadFinished", ...arguments);
|
|
300
|
+
|
|
301
|
+
await this.getImages();
|
|
302
|
+
|
|
303
|
+
this.#dispatchEvent("firmwareUploadProgress", { progress: 100 });
|
|
304
|
+
this.#dispatchEvent("firmwareUploadComplete", {});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#onMcuImageState({ images }: { images?: FirmwareImage[] }) {
|
|
308
|
+
if (images) {
|
|
309
|
+
this.#images = images;
|
|
310
|
+
_console.log("images", this.#images);
|
|
311
|
+
} else {
|
|
312
|
+
_console.log("no images found");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let newStatus: FirmwareStatus = "idle";
|
|
317
|
+
|
|
318
|
+
if (this.#images.length == 2) {
|
|
319
|
+
if (!this.#images[1].bootable) {
|
|
320
|
+
_console.warn('Slot 1 has a invalid image. Click "Erase Image" to erase it or upload a different image');
|
|
321
|
+
} else if (!this.#images[0].confirmed) {
|
|
322
|
+
_console.log(
|
|
323
|
+
'Slot 0 has a valid image. Click "Confirm Image" to confirm it or wait and the device will swap images back.'
|
|
324
|
+
);
|
|
325
|
+
newStatus = "testing";
|
|
326
|
+
} else {
|
|
327
|
+
if (this.#images[1].pending) {
|
|
328
|
+
_console.log("reset to upload to the new firmware image");
|
|
329
|
+
newStatus = "pending";
|
|
330
|
+
} else {
|
|
331
|
+
_console.log("Slot 1 has a valid image. run testImage() to test it or upload a different image.");
|
|
332
|
+
newStatus = "uploaded";
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (this.#images.length == 1) {
|
|
338
|
+
this.#images.push({
|
|
339
|
+
slot: 1,
|
|
340
|
+
empty: true,
|
|
341
|
+
version: "Empty",
|
|
342
|
+
pending: false,
|
|
343
|
+
confirmed: false,
|
|
344
|
+
bootable: false,
|
|
345
|
+
active: false,
|
|
346
|
+
permanent: false,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
_console.log("Select a firmware upload image to upload to slot 1.");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this.#updateStatus(newStatus);
|
|
353
|
+
this.#dispatchEvent("firmwareImages", { firmwareImages: this.#images });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export default FirmwareManager;
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import Device, { SendMessageCallback } from "./Device.ts";
|
|
2
|
+
import { createConsole } from "./utils/Console.ts";
|
|
3
|
+
import EventDispatcher from "./utils/EventDispatcher.ts";
|
|
4
|
+
import { Uint16Max } from "./utils/MathUtils.ts";
|
|
5
|
+
import { textDecoder, textEncoder } from "./utils/Text.ts";
|
|
6
|
+
import autoBind from "auto-bind";
|
|
7
|
+
|
|
8
|
+
const _console = createConsole("InformationManager", { log: true });
|
|
9
|
+
|
|
10
|
+
export const DeviceTypes = ["leftInsole", "rightInsole"] as const;
|
|
11
|
+
export type DeviceType = (typeof DeviceTypes)[number];
|
|
12
|
+
|
|
13
|
+
export const InsoleSides = ["left", "right"] as const;
|
|
14
|
+
export type InsoleSide = (typeof InsoleSides)[number];
|
|
15
|
+
|
|
16
|
+
export const MinNameLength = 2;
|
|
17
|
+
export const MaxNameLength = 30;
|
|
18
|
+
|
|
19
|
+
export const InformationMessageTypes = [
|
|
20
|
+
"isCharging",
|
|
21
|
+
"getBatteryCurrent",
|
|
22
|
+
"getMtu",
|
|
23
|
+
"getId",
|
|
24
|
+
"getName",
|
|
25
|
+
"setName",
|
|
26
|
+
"getType",
|
|
27
|
+
"setType",
|
|
28
|
+
"getCurrentTime",
|
|
29
|
+
"setCurrentTime",
|
|
30
|
+
] as const;
|
|
31
|
+
export type InformationMessageType = (typeof InformationMessageTypes)[number];
|
|
32
|
+
|
|
33
|
+
export const InformationEventTypes = InformationMessageTypes;
|
|
34
|
+
export type InformationEventType = (typeof InformationEventTypes)[number];
|
|
35
|
+
|
|
36
|
+
export interface InformationEventMessages {
|
|
37
|
+
isCharging: { isCharging: boolean };
|
|
38
|
+
getBatteryCurrent: { batteryCurrent: number };
|
|
39
|
+
getMtu: { mtu: number };
|
|
40
|
+
getId: { id: string };
|
|
41
|
+
getName: { name: string };
|
|
42
|
+
getType: { type: DeviceType };
|
|
43
|
+
getCurrentTime: { currentTime: number };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type InformationEventDispatcher = EventDispatcher<Device, InformationEventType, InformationEventMessages>;
|
|
47
|
+
export type SendInformationMessageCallback = SendMessageCallback<InformationMessageType>;
|
|
48
|
+
|
|
49
|
+
class InformationManager {
|
|
50
|
+
constructor() {
|
|
51
|
+
autoBind(this);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
sendMessage!: SendInformationMessageCallback;
|
|
55
|
+
|
|
56
|
+
eventDispatcher!: InformationEventDispatcher;
|
|
57
|
+
get #dispatchEvent() {
|
|
58
|
+
return this.eventDispatcher.dispatchEvent;
|
|
59
|
+
}
|
|
60
|
+
get waitForEvent() {
|
|
61
|
+
return this.eventDispatcher.waitForEvent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// PROPERTIES
|
|
65
|
+
|
|
66
|
+
#isCharging = false;
|
|
67
|
+
get isCharging() {
|
|
68
|
+
return this.#isCharging;
|
|
69
|
+
}
|
|
70
|
+
#updateIsCharging(updatedIsCharging: boolean) {
|
|
71
|
+
_console.assertTypeWithError(updatedIsCharging, "boolean");
|
|
72
|
+
this.#isCharging = updatedIsCharging;
|
|
73
|
+
_console.log({ isCharging: this.#isCharging });
|
|
74
|
+
this.#dispatchEvent("isCharging", { isCharging: this.#isCharging });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#batteryCurrent!: number;
|
|
78
|
+
get batteryCurrent() {
|
|
79
|
+
return this.#batteryCurrent;
|
|
80
|
+
}
|
|
81
|
+
async getBatteryCurrent() {
|
|
82
|
+
_console.log("getting battery current...");
|
|
83
|
+
const promise = this.waitForEvent("getBatteryCurrent");
|
|
84
|
+
this.sendMessage([{ type: "getBatteryCurrent" }]);
|
|
85
|
+
await promise;
|
|
86
|
+
}
|
|
87
|
+
#updateBatteryCurrent(updatedBatteryCurrent: number) {
|
|
88
|
+
_console.assertTypeWithError(updatedBatteryCurrent, "number");
|
|
89
|
+
this.#batteryCurrent = updatedBatteryCurrent;
|
|
90
|
+
_console.log({ batteryCurrent: this.#batteryCurrent });
|
|
91
|
+
this.#dispatchEvent("getBatteryCurrent", { batteryCurrent: this.#batteryCurrent });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#id!: string;
|
|
95
|
+
get id() {
|
|
96
|
+
return this.#id;
|
|
97
|
+
}
|
|
98
|
+
#updateId(updatedId: string) {
|
|
99
|
+
_console.assertTypeWithError(updatedId, "string");
|
|
100
|
+
this.#id = updatedId;
|
|
101
|
+
_console.log({ id: this.#id });
|
|
102
|
+
this.#dispatchEvent("getId", { id: this.#id });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#name = "";
|
|
106
|
+
get name() {
|
|
107
|
+
return this.#name;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
updateName(updatedName: string) {
|
|
111
|
+
_console.assertTypeWithError(updatedName, "string");
|
|
112
|
+
this.#name = updatedName;
|
|
113
|
+
_console.log({ updatedName: this.#name });
|
|
114
|
+
this.#dispatchEvent("getName", { name: this.#name });
|
|
115
|
+
}
|
|
116
|
+
async setName(newName: string) {
|
|
117
|
+
_console.assertTypeWithError(newName, "string");
|
|
118
|
+
_console.assertWithError(
|
|
119
|
+
newName.length >= MinNameLength,
|
|
120
|
+
`name must be greater than ${MinNameLength} characters long ("${newName}" is ${newName.length} characters long)`
|
|
121
|
+
);
|
|
122
|
+
_console.assertWithError(
|
|
123
|
+
newName.length < MaxNameLength,
|
|
124
|
+
`name must be less than ${MaxNameLength} characters long ("${newName}" is ${newName.length} characters long)`
|
|
125
|
+
);
|
|
126
|
+
const setNameData = textEncoder.encode(newName);
|
|
127
|
+
_console.log({ setNameData });
|
|
128
|
+
|
|
129
|
+
const promise = this.waitForEvent("getName");
|
|
130
|
+
this.sendMessage([{ type: "setName", data: setNameData.buffer }]);
|
|
131
|
+
await promise;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// TYPE
|
|
135
|
+
#type!: DeviceType;
|
|
136
|
+
get type() {
|
|
137
|
+
return this.#type;
|
|
138
|
+
}
|
|
139
|
+
get typeEnum() {
|
|
140
|
+
return DeviceTypes.indexOf(this.type);
|
|
141
|
+
}
|
|
142
|
+
#assertValidDeviceType(type: DeviceType) {
|
|
143
|
+
_console.assertEnumWithError(type, DeviceTypes);
|
|
144
|
+
}
|
|
145
|
+
#assertValidDeviceTypeEnum(typeEnum: number) {
|
|
146
|
+
_console.assertTypeWithError(typeEnum, "number");
|
|
147
|
+
_console.assertWithError(typeEnum in DeviceTypes, `invalid typeEnum ${typeEnum}`);
|
|
148
|
+
}
|
|
149
|
+
updateType(updatedType: DeviceType) {
|
|
150
|
+
this.#assertValidDeviceType(updatedType);
|
|
151
|
+
if (updatedType == this.type) {
|
|
152
|
+
_console.log("redundant type assignment");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.#type = updatedType;
|
|
156
|
+
_console.log({ updatedType: this.#type });
|
|
157
|
+
|
|
158
|
+
this.#dispatchEvent("getType", { type: this.#type });
|
|
159
|
+
}
|
|
160
|
+
async #setTypeEnum(newTypeEnum: number) {
|
|
161
|
+
this.#assertValidDeviceTypeEnum(newTypeEnum);
|
|
162
|
+
const setTypeData = Uint8Array.from([newTypeEnum]);
|
|
163
|
+
_console.log({ setTypeData });
|
|
164
|
+
const promise = this.waitForEvent("getType");
|
|
165
|
+
this.sendMessage([{ type: "setType", data: setTypeData.buffer }]);
|
|
166
|
+
await promise;
|
|
167
|
+
}
|
|
168
|
+
async setType(newType: DeviceType) {
|
|
169
|
+
this.#assertValidDeviceType(newType);
|
|
170
|
+
const newTypeEnum = DeviceTypes.indexOf(newType);
|
|
171
|
+
this.#setTypeEnum(newTypeEnum);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get isInsole() {
|
|
175
|
+
switch (this.type) {
|
|
176
|
+
case "leftInsole":
|
|
177
|
+
case "rightInsole":
|
|
178
|
+
return true;
|
|
179
|
+
default:
|
|
180
|
+
// for future non-insole device types
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
get insoleSide(): InsoleSide {
|
|
186
|
+
switch (this.type) {
|
|
187
|
+
case "leftInsole":
|
|
188
|
+
return "left";
|
|
189
|
+
case "rightInsole":
|
|
190
|
+
return "right";
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#mtu = 0;
|
|
195
|
+
get mtu() {
|
|
196
|
+
return this.#mtu;
|
|
197
|
+
}
|
|
198
|
+
#updateMtu(newMtu: number) {
|
|
199
|
+
_console.assertTypeWithError(newMtu, "number");
|
|
200
|
+
if (this.#mtu == newMtu) {
|
|
201
|
+
_console.log("redundant mtu assignment", newMtu);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.#mtu = newMtu;
|
|
205
|
+
|
|
206
|
+
this.#dispatchEvent("getMtu", { mtu: this.#mtu });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#isCurrentTimeSet = false;
|
|
210
|
+
get isCurrentTimeSet() {
|
|
211
|
+
return this.#isCurrentTimeSet;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#onCurrentTime(currentTime: number) {
|
|
215
|
+
_console.log({ currentTime });
|
|
216
|
+
this.#isCurrentTimeSet = currentTime != 0 || Math.abs(Date.now() - currentTime) < Uint16Max;
|
|
217
|
+
if (!this.#isCurrentTimeSet) {
|
|
218
|
+
this.#setCurrentTime();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async #setCurrentTime() {
|
|
222
|
+
_console.log("setting current time...");
|
|
223
|
+
const dataView = new DataView(new ArrayBuffer(8));
|
|
224
|
+
dataView.setBigUint64(0, BigInt(Date.now()), true);
|
|
225
|
+
const promise = this.waitForEvent("getCurrentTime");
|
|
226
|
+
this.sendMessage([{ type: "setCurrentTime", data: dataView.buffer }]);
|
|
227
|
+
await promise;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// MESSAGE
|
|
231
|
+
parseMessage(messageType: InformationMessageType, dataView: DataView) {
|
|
232
|
+
_console.log({ messageType });
|
|
233
|
+
|
|
234
|
+
switch (messageType) {
|
|
235
|
+
case "isCharging":
|
|
236
|
+
const isCharging = Boolean(dataView.getUint8(0));
|
|
237
|
+
_console.log({ isCharging });
|
|
238
|
+
this.#updateIsCharging(isCharging);
|
|
239
|
+
break;
|
|
240
|
+
case "getBatteryCurrent":
|
|
241
|
+
const batteryCurrent = dataView.getFloat32(0, true);
|
|
242
|
+
_console.log({ batteryCurrent });
|
|
243
|
+
this.#updateBatteryCurrent(batteryCurrent);
|
|
244
|
+
break;
|
|
245
|
+
case "getId":
|
|
246
|
+
const id = textDecoder.decode(dataView.buffer);
|
|
247
|
+
_console.log({ id });
|
|
248
|
+
this.#updateId(id);
|
|
249
|
+
break;
|
|
250
|
+
case "getName":
|
|
251
|
+
case "setName":
|
|
252
|
+
const name = textDecoder.decode(dataView.buffer);
|
|
253
|
+
_console.log({ name });
|
|
254
|
+
this.updateName(name);
|
|
255
|
+
break;
|
|
256
|
+
case "getType":
|
|
257
|
+
case "setType":
|
|
258
|
+
const typeEnum = dataView.getUint8(0);
|
|
259
|
+
const type = DeviceTypes[typeEnum];
|
|
260
|
+
_console.log({ typeEnum, type });
|
|
261
|
+
this.updateType(type);
|
|
262
|
+
break;
|
|
263
|
+
case "getMtu":
|
|
264
|
+
const mtu = dataView.getUint16(0, true);
|
|
265
|
+
_console.log({ mtu });
|
|
266
|
+
this.#updateMtu(mtu);
|
|
267
|
+
break;
|
|
268
|
+
case "getCurrentTime":
|
|
269
|
+
case "setCurrentTime":
|
|
270
|
+
const currentTime = Number(dataView.getBigUint64(0, true));
|
|
271
|
+
this.#onCurrentTime(currentTime);
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
throw Error(`uncaught messageType ${messageType}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
clear() {
|
|
279
|
+
this.#isCurrentTimeSet = false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export default InformationManager;
|