@v5x/serial 0.5.5 → 0.5.7
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/README.md +44 -13
- package/dist/Vex.d.ts +3 -1
- package/dist/VexCRC.d.ts +0 -11
- package/dist/VexConnection.d.ts +77 -10
- package/dist/VexDevice.d.ts +32 -2
- package/dist/VexDeviceState.d.ts +39 -17
- package/dist/VexError.d.ts +5 -1
- package/dist/VexEvent.d.ts +14 -10
- package/dist/VexFirmware.d.ts +5 -2
- package/dist/VexFirmwareVersion.d.ts +4 -43
- package/dist/VexIniConfig.d.ts +0 -1
- package/dist/VexPacketBase.d.ts +5 -2
- package/dist/VexPacketCore.d.ts +5 -0
- package/dist/VexPacketEncoder.d.ts +13 -30
- package/dist/VexPacketModels.d.ts +1 -12
- package/dist/VexPacketRegistry.d.ts +3 -0
- package/dist/VexPacketView.d.ts +2 -0
- package/dist/VexScreenCapture.d.ts +2 -0
- package/dist/VexTransfers.d.ts +1 -0
- package/dist/index.cjs +1291 -1818
- package/dist/index.cjs.map +19 -19
- package/dist/index.js +1392 -1872
- package/dist/index.js.map +19 -19
- package/dist/packet-core.cjs +383 -0
- package/dist/packet-core.cjs.map +14 -0
- package/dist/packet-core.js +341 -0
- package/dist/packet-core.js.map +14 -0
- package/package.json +11 -4
package/dist/index.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __returnValue = (v) => v;
|
|
3
|
-
function __exportSetter(name, newValue) {
|
|
4
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
5
|
-
}
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, {
|
|
9
|
-
get: all[name],
|
|
10
|
-
enumerable: true,
|
|
11
|
-
configurable: true,
|
|
12
|
-
set: __exportSetter.bind(all, name)
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
1
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
2
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
3
|
}) : x)(function(x) {
|
|
@@ -105,6 +91,7 @@ var AckType;
|
|
|
105
91
|
AckType2[AckType2["CDC2_NACK_FILE_SYS_FULL"] = 220] = "CDC2_NACK_FILE_SYS_FULL";
|
|
106
92
|
AckType2[AckType2["TIMEOUT"] = 256] = "TIMEOUT";
|
|
107
93
|
AckType2[AckType2["WRITE_ERROR"] = 257] = "WRITE_ERROR";
|
|
94
|
+
AckType2[AckType2["NOT_CONNECTED"] = 258] = "NOT_CONNECTED";
|
|
108
95
|
})(AckType ||= {});
|
|
109
96
|
var SmartDeviceType;
|
|
110
97
|
((SmartDeviceType2) => {
|
|
@@ -200,6 +187,7 @@ var SelectDashScreen;
|
|
|
200
187
|
// src/VexError.ts
|
|
201
188
|
class VexSerialError extends Error {
|
|
202
189
|
kind;
|
|
190
|
+
ackType = undefined;
|
|
203
191
|
constructor(kind, message) {
|
|
204
192
|
super(message);
|
|
205
193
|
this.name = "VexSerialError";
|
|
@@ -222,9 +210,11 @@ class VexInvalidArgumentError extends VexSerialError {
|
|
|
222
210
|
}
|
|
223
211
|
|
|
224
212
|
class VexProtocolError extends VexSerialError {
|
|
225
|
-
|
|
213
|
+
ackType;
|
|
214
|
+
constructor(message, ackType) {
|
|
226
215
|
super("protocol", message);
|
|
227
216
|
this.name = "VexProtocolError";
|
|
217
|
+
this.ackType = ackType;
|
|
228
218
|
}
|
|
229
219
|
}
|
|
230
220
|
|
|
@@ -284,9 +274,26 @@ class VexEventEmitter {
|
|
|
284
274
|
this.handlerMap.set(eventName, listeners);
|
|
285
275
|
}
|
|
286
276
|
emit(eventName, data) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
277
|
+
const listeners = this.handlerMap.get(eventName);
|
|
278
|
+
if (listeners === undefined || listeners.length === 0)
|
|
279
|
+
return;
|
|
280
|
+
if (listeners.length === 1) {
|
|
281
|
+
listeners[0](data);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const errors = [];
|
|
285
|
+
for (const callback of [...listeners]) {
|
|
286
|
+
try {
|
|
287
|
+
callback(data);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
errors.push(error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (errors.length === 1)
|
|
293
|
+
throw errors[0];
|
|
294
|
+
if (errors.length > 1) {
|
|
295
|
+
throw new AggregateError(errors, `listeners for ${String(eventName)} failed`);
|
|
296
|
+
}
|
|
290
297
|
}
|
|
291
298
|
clearListeners() {
|
|
292
299
|
this.handlerMap.clear();
|
|
@@ -299,469 +306,21 @@ class VexEventTarget {
|
|
|
299
306
|
this.emitter = new VexEventEmitter;
|
|
300
307
|
}
|
|
301
308
|
emit(eventName, data) {
|
|
302
|
-
this.emitter.emit(
|
|
309
|
+
this.emitter.emit(eventName, data);
|
|
303
310
|
}
|
|
304
311
|
on(eventName, listener) {
|
|
305
|
-
this.emitter.on(
|
|
312
|
+
this.emitter.on(eventName, listener);
|
|
306
313
|
}
|
|
307
314
|
remove(eventName, listener) {
|
|
308
|
-
this.emitter.remove(
|
|
315
|
+
this.emitter.remove(eventName, listener);
|
|
309
316
|
}
|
|
310
317
|
clearListeners() {
|
|
311
318
|
this.emitter.clearListeners();
|
|
312
319
|
}
|
|
313
320
|
}
|
|
314
321
|
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
withStackTrace: false
|
|
318
|
-
};
|
|
319
|
-
var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
|
|
320
|
-
const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
|
|
321
|
-
const maybeStack = config.withStackTrace ? new Error().stack : undefined;
|
|
322
|
-
return {
|
|
323
|
-
data,
|
|
324
|
-
message,
|
|
325
|
-
stack: maybeStack
|
|
326
|
-
};
|
|
327
|
-
};
|
|
328
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
329
|
-
function adopt(value) {
|
|
330
|
-
return value instanceof P ? value : new P(function(resolve) {
|
|
331
|
-
resolve(value);
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
return new (P || (P = Promise))(function(resolve, reject) {
|
|
335
|
-
function fulfilled(value) {
|
|
336
|
-
try {
|
|
337
|
-
step(generator.next(value));
|
|
338
|
-
} catch (e) {
|
|
339
|
-
reject(e);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
function rejected(value) {
|
|
343
|
-
try {
|
|
344
|
-
step(generator["throw"](value));
|
|
345
|
-
} catch (e) {
|
|
346
|
-
reject(e);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
function step(result) {
|
|
350
|
-
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
351
|
-
}
|
|
352
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
function __values(o) {
|
|
356
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
357
|
-
if (m)
|
|
358
|
-
return m.call(o);
|
|
359
|
-
if (o && typeof o.length === "number")
|
|
360
|
-
return {
|
|
361
|
-
next: function() {
|
|
362
|
-
if (o && i >= o.length)
|
|
363
|
-
o = undefined;
|
|
364
|
-
return { value: o && o[i++], done: !o };
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
368
|
-
}
|
|
369
|
-
function __await(v) {
|
|
370
|
-
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
371
|
-
}
|
|
372
|
-
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
373
|
-
if (!Symbol.asyncIterator)
|
|
374
|
-
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
375
|
-
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
376
|
-
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
|
|
377
|
-
return this;
|
|
378
|
-
}, i;
|
|
379
|
-
function awaitReturn(f) {
|
|
380
|
-
return function(v) {
|
|
381
|
-
return Promise.resolve(v).then(f, reject);
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
function verb(n, f) {
|
|
385
|
-
if (g[n]) {
|
|
386
|
-
i[n] = function(v) {
|
|
387
|
-
return new Promise(function(a, b) {
|
|
388
|
-
q.push([n, v, a, b]) > 1 || resume(n, v);
|
|
389
|
-
});
|
|
390
|
-
};
|
|
391
|
-
if (f)
|
|
392
|
-
i[n] = f(i[n]);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
function resume(n, v) {
|
|
396
|
-
try {
|
|
397
|
-
step(g[n](v));
|
|
398
|
-
} catch (e) {
|
|
399
|
-
settle(q[0][3], e);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
function step(r) {
|
|
403
|
-
r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
|
|
404
|
-
}
|
|
405
|
-
function fulfill(value) {
|
|
406
|
-
resume("next", value);
|
|
407
|
-
}
|
|
408
|
-
function reject(value) {
|
|
409
|
-
resume("throw", value);
|
|
410
|
-
}
|
|
411
|
-
function settle(f, v) {
|
|
412
|
-
if (f(v), q.shift(), q.length)
|
|
413
|
-
resume(q[0][0], q[0][1]);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
function __asyncDelegator(o) {
|
|
417
|
-
var i, p;
|
|
418
|
-
return i = {}, verb("next"), verb("throw", function(e) {
|
|
419
|
-
throw e;
|
|
420
|
-
}), verb("return"), i[Symbol.iterator] = function() {
|
|
421
|
-
return this;
|
|
422
|
-
}, i;
|
|
423
|
-
function verb(n, f) {
|
|
424
|
-
i[n] = o[n] ? function(v) {
|
|
425
|
-
return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
|
|
426
|
-
} : f;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
function __asyncValues(o) {
|
|
430
|
-
if (!Symbol.asyncIterator)
|
|
431
|
-
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
432
|
-
var m = o[Symbol.asyncIterator], i;
|
|
433
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
|
|
434
|
-
return this;
|
|
435
|
-
}, i);
|
|
436
|
-
function verb(n) {
|
|
437
|
-
i[n] = o[n] && function(v) {
|
|
438
|
-
return new Promise(function(resolve, reject) {
|
|
439
|
-
v = o[n](v), settle(resolve, reject, v.done, v.value);
|
|
440
|
-
});
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
function settle(resolve, reject, d, v) {
|
|
444
|
-
Promise.resolve(v).then(function(v2) {
|
|
445
|
-
resolve({ value: v2, done: d });
|
|
446
|
-
}, reject);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
class ResultAsync {
|
|
450
|
-
constructor(res) {
|
|
451
|
-
this._promise = res;
|
|
452
|
-
}
|
|
453
|
-
static fromSafePromise(promise) {
|
|
454
|
-
const newPromise = promise.then((value) => new Ok(value));
|
|
455
|
-
return new ResultAsync(newPromise);
|
|
456
|
-
}
|
|
457
|
-
static fromPromise(promise, errorFn) {
|
|
458
|
-
const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
|
|
459
|
-
return new ResultAsync(newPromise);
|
|
460
|
-
}
|
|
461
|
-
static fromThrowable(fn, errorFn) {
|
|
462
|
-
return (...args) => {
|
|
463
|
-
return new ResultAsync((() => __awaiter(this, undefined, undefined, function* () {
|
|
464
|
-
try {
|
|
465
|
-
return new Ok(yield fn(...args));
|
|
466
|
-
} catch (error) {
|
|
467
|
-
return new Err(errorFn ? errorFn(error) : error);
|
|
468
|
-
}
|
|
469
|
-
}))());
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
static combine(asyncResultList) {
|
|
473
|
-
return combineResultAsyncList(asyncResultList);
|
|
474
|
-
}
|
|
475
|
-
static combineWithAllErrors(asyncResultList) {
|
|
476
|
-
return combineResultAsyncListWithAllErrors(asyncResultList);
|
|
477
|
-
}
|
|
478
|
-
map(f) {
|
|
479
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
480
|
-
if (res.isErr()) {
|
|
481
|
-
return new Err(res.error);
|
|
482
|
-
}
|
|
483
|
-
return new Ok(yield f(res.value));
|
|
484
|
-
})));
|
|
485
|
-
}
|
|
486
|
-
andThrough(f) {
|
|
487
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
488
|
-
if (res.isErr()) {
|
|
489
|
-
return new Err(res.error);
|
|
490
|
-
}
|
|
491
|
-
const newRes = yield f(res.value);
|
|
492
|
-
if (newRes.isErr()) {
|
|
493
|
-
return new Err(newRes.error);
|
|
494
|
-
}
|
|
495
|
-
return new Ok(res.value);
|
|
496
|
-
})));
|
|
497
|
-
}
|
|
498
|
-
andTee(f) {
|
|
499
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
500
|
-
if (res.isErr()) {
|
|
501
|
-
return new Err(res.error);
|
|
502
|
-
}
|
|
503
|
-
try {
|
|
504
|
-
yield f(res.value);
|
|
505
|
-
} catch (e) {}
|
|
506
|
-
return new Ok(res.value);
|
|
507
|
-
})));
|
|
508
|
-
}
|
|
509
|
-
orTee(f) {
|
|
510
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
511
|
-
if (res.isOk()) {
|
|
512
|
-
return new Ok(res.value);
|
|
513
|
-
}
|
|
514
|
-
try {
|
|
515
|
-
yield f(res.error);
|
|
516
|
-
} catch (e) {}
|
|
517
|
-
return new Err(res.error);
|
|
518
|
-
})));
|
|
519
|
-
}
|
|
520
|
-
mapErr(f) {
|
|
521
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
522
|
-
if (res.isOk()) {
|
|
523
|
-
return new Ok(res.value);
|
|
524
|
-
}
|
|
525
|
-
return new Err(yield f(res.error));
|
|
526
|
-
})));
|
|
527
|
-
}
|
|
528
|
-
andThen(f) {
|
|
529
|
-
return new ResultAsync(this._promise.then((res) => {
|
|
530
|
-
if (res.isErr()) {
|
|
531
|
-
return new Err(res.error);
|
|
532
|
-
}
|
|
533
|
-
const newValue = f(res.value);
|
|
534
|
-
return newValue instanceof ResultAsync ? newValue._promise : newValue;
|
|
535
|
-
}));
|
|
536
|
-
}
|
|
537
|
-
orElse(f) {
|
|
538
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
539
|
-
if (res.isErr()) {
|
|
540
|
-
return f(res.error);
|
|
541
|
-
}
|
|
542
|
-
return new Ok(res.value);
|
|
543
|
-
})));
|
|
544
|
-
}
|
|
545
|
-
match(ok, _err) {
|
|
546
|
-
return this._promise.then((res) => res.match(ok, _err));
|
|
547
|
-
}
|
|
548
|
-
unwrapOr(t) {
|
|
549
|
-
return this._promise.then((res) => res.unwrapOr(t));
|
|
550
|
-
}
|
|
551
|
-
safeUnwrap() {
|
|
552
|
-
return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
|
|
553
|
-
return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
then(successCallback, failureCallback) {
|
|
557
|
-
return this._promise.then(successCallback, failureCallback);
|
|
558
|
-
}
|
|
559
|
-
[Symbol.asyncIterator]() {
|
|
560
|
-
return __asyncGenerator(this, arguments, function* _a() {
|
|
561
|
-
const result = yield __await(this._promise);
|
|
562
|
-
if (result.isErr()) {
|
|
563
|
-
yield yield __await(errAsync(result.error));
|
|
564
|
-
}
|
|
565
|
-
return yield __await(result.value);
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
function errAsync(err) {
|
|
570
|
-
return new ResultAsync(Promise.resolve(new Err(err)));
|
|
571
|
-
}
|
|
572
|
-
var fromPromise = ResultAsync.fromPromise;
|
|
573
|
-
var fromSafePromise = ResultAsync.fromSafePromise;
|
|
574
|
-
var fromAsyncThrowable = ResultAsync.fromThrowable;
|
|
575
|
-
var combineResultList = (resultList) => {
|
|
576
|
-
let acc = ok([]);
|
|
577
|
-
for (const result of resultList) {
|
|
578
|
-
if (result.isErr()) {
|
|
579
|
-
acc = err(result.error);
|
|
580
|
-
break;
|
|
581
|
-
} else {
|
|
582
|
-
acc.map((list) => list.push(result.value));
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
return acc;
|
|
586
|
-
};
|
|
587
|
-
var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
|
|
588
|
-
var combineResultListWithAllErrors = (resultList) => {
|
|
589
|
-
let acc = ok([]);
|
|
590
|
-
for (const result of resultList) {
|
|
591
|
-
if (result.isErr() && acc.isErr()) {
|
|
592
|
-
acc.error.push(result.error);
|
|
593
|
-
} else if (result.isErr() && acc.isOk()) {
|
|
594
|
-
acc = err([result.error]);
|
|
595
|
-
} else if (result.isOk() && acc.isOk()) {
|
|
596
|
-
acc.value.push(result.value);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return acc;
|
|
600
|
-
};
|
|
601
|
-
var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
|
|
602
|
-
var Result;
|
|
603
|
-
(function(Result2) {
|
|
604
|
-
function fromThrowable(fn, errorFn) {
|
|
605
|
-
return (...args) => {
|
|
606
|
-
try {
|
|
607
|
-
const result = fn(...args);
|
|
608
|
-
return ok(result);
|
|
609
|
-
} catch (e) {
|
|
610
|
-
return err(errorFn ? errorFn(e) : e);
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
Result2.fromThrowable = fromThrowable;
|
|
615
|
-
function combine(resultList) {
|
|
616
|
-
return combineResultList(resultList);
|
|
617
|
-
}
|
|
618
|
-
Result2.combine = combine;
|
|
619
|
-
function combineWithAllErrors(resultList) {
|
|
620
|
-
return combineResultListWithAllErrors(resultList);
|
|
621
|
-
}
|
|
622
|
-
Result2.combineWithAllErrors = combineWithAllErrors;
|
|
623
|
-
})(Result || (Result = {}));
|
|
624
|
-
function ok(value) {
|
|
625
|
-
return new Ok(value);
|
|
626
|
-
}
|
|
627
|
-
function err(err2) {
|
|
628
|
-
return new Err(err2);
|
|
629
|
-
}
|
|
630
|
-
class Ok {
|
|
631
|
-
constructor(value) {
|
|
632
|
-
this.value = value;
|
|
633
|
-
}
|
|
634
|
-
isOk() {
|
|
635
|
-
return true;
|
|
636
|
-
}
|
|
637
|
-
isErr() {
|
|
638
|
-
return !this.isOk();
|
|
639
|
-
}
|
|
640
|
-
map(f) {
|
|
641
|
-
return ok(f(this.value));
|
|
642
|
-
}
|
|
643
|
-
mapErr(_f) {
|
|
644
|
-
return ok(this.value);
|
|
645
|
-
}
|
|
646
|
-
andThen(f) {
|
|
647
|
-
return f(this.value);
|
|
648
|
-
}
|
|
649
|
-
andThrough(f) {
|
|
650
|
-
return f(this.value).map((_value) => this.value);
|
|
651
|
-
}
|
|
652
|
-
andTee(f) {
|
|
653
|
-
try {
|
|
654
|
-
f(this.value);
|
|
655
|
-
} catch (e) {}
|
|
656
|
-
return ok(this.value);
|
|
657
|
-
}
|
|
658
|
-
orTee(_f) {
|
|
659
|
-
return ok(this.value);
|
|
660
|
-
}
|
|
661
|
-
orElse(_f) {
|
|
662
|
-
return ok(this.value);
|
|
663
|
-
}
|
|
664
|
-
asyncAndThen(f) {
|
|
665
|
-
return f(this.value);
|
|
666
|
-
}
|
|
667
|
-
asyncAndThrough(f) {
|
|
668
|
-
return f(this.value).map(() => this.value);
|
|
669
|
-
}
|
|
670
|
-
asyncMap(f) {
|
|
671
|
-
return ResultAsync.fromSafePromise(f(this.value));
|
|
672
|
-
}
|
|
673
|
-
unwrapOr(_v) {
|
|
674
|
-
return this.value;
|
|
675
|
-
}
|
|
676
|
-
match(ok2, _err) {
|
|
677
|
-
return ok2(this.value);
|
|
678
|
-
}
|
|
679
|
-
safeUnwrap() {
|
|
680
|
-
const value = this.value;
|
|
681
|
-
return function* () {
|
|
682
|
-
return value;
|
|
683
|
-
}();
|
|
684
|
-
}
|
|
685
|
-
_unsafeUnwrap(_) {
|
|
686
|
-
return this.value;
|
|
687
|
-
}
|
|
688
|
-
_unsafeUnwrapErr(config) {
|
|
689
|
-
throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
|
|
690
|
-
}
|
|
691
|
-
*[Symbol.iterator]() {
|
|
692
|
-
return this.value;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
class Err {
|
|
697
|
-
constructor(error) {
|
|
698
|
-
this.error = error;
|
|
699
|
-
}
|
|
700
|
-
isOk() {
|
|
701
|
-
return false;
|
|
702
|
-
}
|
|
703
|
-
isErr() {
|
|
704
|
-
return !this.isOk();
|
|
705
|
-
}
|
|
706
|
-
map(_f) {
|
|
707
|
-
return err(this.error);
|
|
708
|
-
}
|
|
709
|
-
mapErr(f) {
|
|
710
|
-
return err(f(this.error));
|
|
711
|
-
}
|
|
712
|
-
andThrough(_f) {
|
|
713
|
-
return err(this.error);
|
|
714
|
-
}
|
|
715
|
-
andTee(_f) {
|
|
716
|
-
return err(this.error);
|
|
717
|
-
}
|
|
718
|
-
orTee(f) {
|
|
719
|
-
try {
|
|
720
|
-
f(this.error);
|
|
721
|
-
} catch (e) {}
|
|
722
|
-
return err(this.error);
|
|
723
|
-
}
|
|
724
|
-
andThen(_f) {
|
|
725
|
-
return err(this.error);
|
|
726
|
-
}
|
|
727
|
-
orElse(f) {
|
|
728
|
-
return f(this.error);
|
|
729
|
-
}
|
|
730
|
-
asyncAndThen(_f) {
|
|
731
|
-
return errAsync(this.error);
|
|
732
|
-
}
|
|
733
|
-
asyncAndThrough(_f) {
|
|
734
|
-
return errAsync(this.error);
|
|
735
|
-
}
|
|
736
|
-
asyncMap(_f) {
|
|
737
|
-
return errAsync(this.error);
|
|
738
|
-
}
|
|
739
|
-
unwrapOr(v) {
|
|
740
|
-
return v;
|
|
741
|
-
}
|
|
742
|
-
match(_ok, err2) {
|
|
743
|
-
return err2(this.error);
|
|
744
|
-
}
|
|
745
|
-
safeUnwrap() {
|
|
746
|
-
const error = this.error;
|
|
747
|
-
return function* () {
|
|
748
|
-
yield err(error);
|
|
749
|
-
throw new Error("Do not use this generator out of `safeTry`");
|
|
750
|
-
}();
|
|
751
|
-
}
|
|
752
|
-
_unsafeUnwrap(config) {
|
|
753
|
-
throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
|
|
754
|
-
}
|
|
755
|
-
_unsafeUnwrapErr(_) {
|
|
756
|
-
return this.error;
|
|
757
|
-
}
|
|
758
|
-
*[Symbol.iterator]() {
|
|
759
|
-
const self = this;
|
|
760
|
-
yield self;
|
|
761
|
-
return self;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
var fromThrowable = Result.fromThrowable;
|
|
322
|
+
// src/VexConnection.ts
|
|
323
|
+
import { err, ok, ResultAsync } from "neverthrow";
|
|
765
324
|
|
|
766
325
|
// src/VexPacketBase.ts
|
|
767
326
|
var MATCH_STATUS_ALT_ACK = 167;
|
|
@@ -775,42 +334,32 @@ class Packet {
|
|
|
775
334
|
}
|
|
776
335
|
|
|
777
336
|
class DeviceBoundPacket extends Packet {
|
|
337
|
+
static COMMAND_ID;
|
|
338
|
+
static COMMAND_EXTENDED_ID;
|
|
778
339
|
get commandId() {
|
|
779
340
|
return this.constructor.COMMAND_ID;
|
|
780
341
|
}
|
|
781
342
|
get commandExtendedId() {
|
|
782
343
|
return this.constructor.COMMAND_EXTENDED_ID;
|
|
783
344
|
}
|
|
784
|
-
static COMMAND_ID;
|
|
785
|
-
static COMMAND_EXTENDED_ID;
|
|
786
345
|
constructor(payload) {
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
if (payload === undefined) {
|
|
791
|
-
this.data = Packet.ENCODER.cdcCommand(me.COMMAND_ID);
|
|
792
|
-
} else {
|
|
793
|
-
this.data = Packet.ENCODER.cdcCommandWithData(me.COMMAND_ID, payload);
|
|
794
|
-
}
|
|
795
|
-
} else {
|
|
796
|
-
if (payload === undefined) {
|
|
797
|
-
this.data = Packet.ENCODER.cdc2Command(me.COMMAND_ID, me.COMMAND_EXTENDED_ID);
|
|
798
|
-
} else {
|
|
799
|
-
this.data = Packet.ENCODER.cdc2CommandWithData(me.COMMAND_ID, me.COMMAND_EXTENDED_ID, payload);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
346
|
+
const { COMMAND_ID: cmd, COMMAND_EXTENDED_ID: ext } = new.target;
|
|
347
|
+
const encoder = Packet.ENCODER;
|
|
348
|
+
super(ext === undefined ? payload === undefined ? encoder.cdcCommand(cmd) : encoder.cdcCommandWithData(cmd, payload) : payload === undefined ? encoder.cdc2Command(cmd, ext) : encoder.cdc2CommandWithData(cmd, ext, payload));
|
|
802
349
|
}
|
|
803
350
|
}
|
|
804
351
|
|
|
805
352
|
class HostBoundPacket extends Packet {
|
|
806
|
-
|
|
353
|
+
static COMMAND_ID;
|
|
354
|
+
static COMMAND_EXTENDED_ID;
|
|
355
|
+
ack;
|
|
807
356
|
payloadSize;
|
|
808
357
|
ackIndex;
|
|
809
358
|
constructor(data) {
|
|
810
359
|
super(data);
|
|
811
360
|
this.payloadSize = Packet.ENCODER.getPayloadSize(this.data);
|
|
812
|
-
|
|
813
|
-
this.ack = this.data[this.ackIndex
|
|
361
|
+
this.ackIndex = Packet.ENCODER.getHostHeaderLength(this.data) + 1;
|
|
362
|
+
this.ack = this.data[this.ackIndex];
|
|
814
363
|
}
|
|
815
364
|
static isValidPacket(data, n) {
|
|
816
365
|
const ack = data[n + 1];
|
|
@@ -819,385 +368,162 @@ class HostBoundPacket extends Packet {
|
|
|
819
368
|
}
|
|
820
369
|
|
|
821
370
|
// src/VexCRC.ts
|
|
822
|
-
var
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
33560,
|
|
849
|
-
45947,
|
|
850
|
-
41818,
|
|
851
|
-
54205,
|
|
852
|
-
50076,
|
|
853
|
-
62463,
|
|
854
|
-
58334,
|
|
855
|
-
9314,
|
|
856
|
-
13379,
|
|
857
|
-
1056,
|
|
858
|
-
5121,
|
|
859
|
-
25830,
|
|
860
|
-
29895,
|
|
861
|
-
17572,
|
|
862
|
-
21637,
|
|
863
|
-
42346,
|
|
864
|
-
46411,
|
|
865
|
-
34088,
|
|
866
|
-
38153,
|
|
867
|
-
58862,
|
|
868
|
-
62927,
|
|
869
|
-
50604,
|
|
870
|
-
54669,
|
|
871
|
-
13907,
|
|
872
|
-
9842,
|
|
873
|
-
5649,
|
|
874
|
-
1584,
|
|
875
|
-
30423,
|
|
876
|
-
26358,
|
|
877
|
-
22165,
|
|
878
|
-
18100,
|
|
879
|
-
46939,
|
|
880
|
-
42874,
|
|
881
|
-
38681,
|
|
882
|
-
34616,
|
|
883
|
-
63455,
|
|
884
|
-
59390,
|
|
885
|
-
55197,
|
|
886
|
-
51132,
|
|
887
|
-
18628,
|
|
888
|
-
22757,
|
|
889
|
-
26758,
|
|
890
|
-
30887,
|
|
891
|
-
2112,
|
|
892
|
-
6241,
|
|
893
|
-
10242,
|
|
894
|
-
14371,
|
|
895
|
-
51660,
|
|
896
|
-
55789,
|
|
897
|
-
59790,
|
|
898
|
-
63919,
|
|
899
|
-
35144,
|
|
900
|
-
39273,
|
|
901
|
-
43274,
|
|
902
|
-
47403,
|
|
903
|
-
23285,
|
|
904
|
-
19156,
|
|
905
|
-
31415,
|
|
906
|
-
27286,
|
|
907
|
-
6769,
|
|
908
|
-
2640,
|
|
909
|
-
14899,
|
|
910
|
-
10770,
|
|
911
|
-
56317,
|
|
912
|
-
52188,
|
|
913
|
-
64447,
|
|
914
|
-
60318,
|
|
915
|
-
39801,
|
|
916
|
-
35672,
|
|
917
|
-
47931,
|
|
918
|
-
43802,
|
|
919
|
-
27814,
|
|
920
|
-
31879,
|
|
921
|
-
19684,
|
|
922
|
-
23749,
|
|
923
|
-
11298,
|
|
924
|
-
15363,
|
|
925
|
-
3168,
|
|
926
|
-
7233,
|
|
927
|
-
60846,
|
|
928
|
-
64911,
|
|
929
|
-
52716,
|
|
930
|
-
56781,
|
|
931
|
-
44330,
|
|
932
|
-
48395,
|
|
933
|
-
36200,
|
|
934
|
-
40265,
|
|
935
|
-
32407,
|
|
936
|
-
28342,
|
|
937
|
-
24277,
|
|
938
|
-
20212,
|
|
939
|
-
15891,
|
|
940
|
-
11826,
|
|
941
|
-
7761,
|
|
942
|
-
3696,
|
|
943
|
-
65439,
|
|
944
|
-
61374,
|
|
945
|
-
57309,
|
|
946
|
-
53244,
|
|
947
|
-
48923,
|
|
948
|
-
44858,
|
|
949
|
-
40793,
|
|
950
|
-
36728,
|
|
951
|
-
37256,
|
|
952
|
-
33193,
|
|
953
|
-
45514,
|
|
954
|
-
41451,
|
|
955
|
-
53516,
|
|
956
|
-
49453,
|
|
957
|
-
61774,
|
|
958
|
-
57711,
|
|
959
|
-
4224,
|
|
960
|
-
161,
|
|
961
|
-
12482,
|
|
962
|
-
8419,
|
|
963
|
-
20484,
|
|
964
|
-
16421,
|
|
965
|
-
28742,
|
|
966
|
-
24679,
|
|
967
|
-
33721,
|
|
968
|
-
37784,
|
|
969
|
-
41979,
|
|
970
|
-
46042,
|
|
971
|
-
49981,
|
|
972
|
-
54044,
|
|
973
|
-
58239,
|
|
974
|
-
62302,
|
|
975
|
-
689,
|
|
976
|
-
4752,
|
|
977
|
-
8947,
|
|
978
|
-
13010,
|
|
979
|
-
16949,
|
|
980
|
-
21012,
|
|
981
|
-
25207,
|
|
982
|
-
29270,
|
|
983
|
-
46570,
|
|
984
|
-
42443,
|
|
985
|
-
38312,
|
|
986
|
-
34185,
|
|
987
|
-
62830,
|
|
988
|
-
58703,
|
|
989
|
-
54572,
|
|
990
|
-
50445,
|
|
991
|
-
13538,
|
|
992
|
-
9411,
|
|
993
|
-
5280,
|
|
994
|
-
1153,
|
|
995
|
-
29798,
|
|
996
|
-
25671,
|
|
997
|
-
21540,
|
|
998
|
-
17413,
|
|
999
|
-
42971,
|
|
1000
|
-
47098,
|
|
1001
|
-
34713,
|
|
1002
|
-
38840,
|
|
1003
|
-
59231,
|
|
1004
|
-
63358,
|
|
1005
|
-
50973,
|
|
1006
|
-
55100,
|
|
1007
|
-
9939,
|
|
1008
|
-
14066,
|
|
1009
|
-
1681,
|
|
1010
|
-
5808,
|
|
1011
|
-
26199,
|
|
1012
|
-
30326,
|
|
1013
|
-
17941,
|
|
1014
|
-
22068,
|
|
1015
|
-
55628,
|
|
1016
|
-
51565,
|
|
1017
|
-
63758,
|
|
1018
|
-
59695,
|
|
1019
|
-
39368,
|
|
1020
|
-
35305,
|
|
1021
|
-
47498,
|
|
1022
|
-
43435,
|
|
1023
|
-
22596,
|
|
1024
|
-
18533,
|
|
1025
|
-
30726,
|
|
1026
|
-
26663,
|
|
1027
|
-
6336,
|
|
1028
|
-
2273,
|
|
1029
|
-
14466,
|
|
1030
|
-
10403,
|
|
1031
|
-
52093,
|
|
1032
|
-
56156,
|
|
1033
|
-
60223,
|
|
1034
|
-
64286,
|
|
1035
|
-
35833,
|
|
1036
|
-
39896,
|
|
1037
|
-
43963,
|
|
1038
|
-
48026,
|
|
1039
|
-
19061,
|
|
1040
|
-
23124,
|
|
1041
|
-
27191,
|
|
1042
|
-
31254,
|
|
1043
|
-
2801,
|
|
1044
|
-
6864,
|
|
1045
|
-
10931,
|
|
1046
|
-
14994,
|
|
1047
|
-
64814,
|
|
1048
|
-
60687,
|
|
1049
|
-
56684,
|
|
1050
|
-
52557,
|
|
1051
|
-
48554,
|
|
1052
|
-
44427,
|
|
1053
|
-
40424,
|
|
1054
|
-
36297,
|
|
1055
|
-
31782,
|
|
1056
|
-
27655,
|
|
1057
|
-
23652,
|
|
1058
|
-
19525,
|
|
1059
|
-
15522,
|
|
1060
|
-
11395,
|
|
1061
|
-
7392,
|
|
1062
|
-
3265,
|
|
1063
|
-
61215,
|
|
1064
|
-
65342,
|
|
1065
|
-
53085,
|
|
1066
|
-
57212,
|
|
1067
|
-
44955,
|
|
1068
|
-
49082,
|
|
1069
|
-
36825,
|
|
1070
|
-
40952,
|
|
1071
|
-
28183,
|
|
1072
|
-
32310,
|
|
1073
|
-
20053,
|
|
1074
|
-
24180,
|
|
1075
|
-
11923,
|
|
1076
|
-
16050,
|
|
1077
|
-
3793,
|
|
1078
|
-
7920
|
|
1079
|
-
];
|
|
371
|
+
var POLYNOMIAL_CRC32 = 79764919;
|
|
372
|
+
var POLYNOMIAL_CRC16 = 4129;
|
|
373
|
+
function genTable16() {
|
|
374
|
+
const table = new Uint16Array(256);
|
|
375
|
+
for (let i = 0;i < 256; i++) {
|
|
376
|
+
let acc = i << 8;
|
|
377
|
+
for (let j = 0;j < 8; j++) {
|
|
378
|
+
acc = (acc & 32768) !== 0 ? acc << 1 ^ POLYNOMIAL_CRC16 : acc << 1;
|
|
379
|
+
}
|
|
380
|
+
table[i] = acc & 65535;
|
|
381
|
+
}
|
|
382
|
+
return table;
|
|
383
|
+
}
|
|
384
|
+
function genTable32() {
|
|
385
|
+
const table = new Uint32Array(256);
|
|
386
|
+
for (let i = 0;i < 256; i++) {
|
|
387
|
+
let acc = i << 24;
|
|
388
|
+
for (let j = 0;j < 8; j++) {
|
|
389
|
+
acc = (acc & 2147483648) !== 0 ? acc << 1 ^ POLYNOMIAL_CRC32 : acc << 1;
|
|
390
|
+
}
|
|
391
|
+
table[i] = acc;
|
|
392
|
+
}
|
|
393
|
+
return table;
|
|
394
|
+
}
|
|
395
|
+
var CRC16_TABLE = genTable16();
|
|
396
|
+
var CRC32_TABLE = genTable32();
|
|
1080
397
|
|
|
1081
398
|
class CrcGenerator {
|
|
1082
|
-
crc32Table;
|
|
1083
|
-
static POLYNOMIAL_CRC32 =
|
|
1084
|
-
static POLYNOMIAL_CRC16 =
|
|
1085
|
-
constructor() {
|
|
1086
|
-
this.crc32Table = new Uint32Array(256);
|
|
1087
|
-
this.crc32GenTable();
|
|
1088
|
-
}
|
|
399
|
+
crc32Table = CRC32_TABLE;
|
|
400
|
+
static POLYNOMIAL_CRC32 = POLYNOMIAL_CRC32;
|
|
401
|
+
static POLYNOMIAL_CRC16 = POLYNOMIAL_CRC16;
|
|
1089
402
|
crc16(buf, initValue) {
|
|
1090
|
-
|
|
1091
|
-
let
|
|
1092
|
-
|
|
1093
|
-
let j;
|
|
1094
|
-
for (j = 0;j < numberOfBytes; j++) {
|
|
1095
|
-
i = (accumulator >>> 8 ^ buf[j]) & 255;
|
|
1096
|
-
accumulator = (accumulator << 8 ^ CRC16TABLE[i]) >>> 0;
|
|
1097
|
-
}
|
|
1098
|
-
return (accumulator & 65535) >>> 0;
|
|
1099
|
-
}
|
|
1100
|
-
crc32GenTable() {
|
|
1101
|
-
let i;
|
|
1102
|
-
let j;
|
|
1103
|
-
let crcAccumulator;
|
|
1104
|
-
for (i = 0;i < 256; i++) {
|
|
1105
|
-
crcAccumulator = i << 24;
|
|
1106
|
-
for (j = 0;j < 8; j++) {
|
|
1107
|
-
if ((crcAccumulator & 2147483648) !== 0)
|
|
1108
|
-
crcAccumulator = crcAccumulator << 1 ^ CrcGenerator.POLYNOMIAL_CRC32;
|
|
1109
|
-
else
|
|
1110
|
-
crcAccumulator = crcAccumulator << 1;
|
|
1111
|
-
}
|
|
1112
|
-
this.crc32Table[i] = crcAccumulator;
|
|
403
|
+
let acc = initValue;
|
|
404
|
+
for (let j = 0;j < buf.length; j++) {
|
|
405
|
+
acc = (acc << 8 ^ CRC16_TABLE[(acc >>> 8 ^ buf[j]) & 255]) >>> 0;
|
|
1113
406
|
}
|
|
407
|
+
return acc & 65535;
|
|
1114
408
|
}
|
|
1115
409
|
crc32(buf, initValue) {
|
|
1116
|
-
|
|
1117
|
-
let
|
|
1118
|
-
|
|
1119
|
-
let j;
|
|
1120
|
-
for (j = 0;j < numberOfBytes; j++) {
|
|
1121
|
-
i = (crcAccumulator >>> 24 ^ buf[j]) & 255;
|
|
1122
|
-
crcAccumulator = (crcAccumulator << 8 ^ this.crc32Table[i]) >>> 0;
|
|
410
|
+
let acc = initValue;
|
|
411
|
+
for (let j = 0;j < buf.length; j++) {
|
|
412
|
+
acc = (acc << 8 ^ CRC32_TABLE[(acc >>> 24 ^ buf[j]) & 255]) >>> 0;
|
|
1123
413
|
}
|
|
1124
|
-
return
|
|
414
|
+
return acc >>> 0;
|
|
1125
415
|
}
|
|
1126
416
|
}
|
|
1127
417
|
|
|
1128
|
-
// src/
|
|
1129
|
-
var
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
}
|
|
418
|
+
// src/VexPacketEncoder.ts
|
|
419
|
+
var textEncoder = new TextEncoder;
|
|
420
|
+
var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
|
|
421
|
+
function encodeFixedText(value, field, maxBytes) {
|
|
422
|
+
const encoded = textEncoder.encode(value);
|
|
423
|
+
if (encoded.byteLength > maxBytes) {
|
|
424
|
+
throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
|
|
425
|
+
}
|
|
426
|
+
return encoded;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
class PacketEncoder {
|
|
430
|
+
static HEADERS_LENGTH = 4;
|
|
431
|
+
static HEADER_TO_DEVICE = [201, 54, 184, 71];
|
|
432
|
+
static HEADER_TO_HOST = [170, 85];
|
|
433
|
+
static J2000_EPOCH = 946684800;
|
|
434
|
+
vexVersion = 0;
|
|
435
|
+
crcgen = new CrcGenerator;
|
|
436
|
+
allPacketsTable = new Map;
|
|
437
|
+
static getInstance() {
|
|
438
|
+
Packet.ENCODER ??= new PacketEncoder;
|
|
439
|
+
return Packet.ENCODER;
|
|
440
|
+
}
|
|
441
|
+
constructor() {}
|
|
442
|
+
registerPacketTypes(types) {
|
|
443
|
+
for (const type of types) {
|
|
444
|
+
let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
|
|
445
|
+
if (byExtendedId === undefined) {
|
|
446
|
+
byExtendedId = new Map;
|
|
447
|
+
this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
|
|
448
|
+
}
|
|
449
|
+
byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
getPacketType(commandId, commandExtendedId) {
|
|
453
|
+
if (commandId === undefined)
|
|
454
|
+
return;
|
|
455
|
+
return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
|
|
456
|
+
}
|
|
457
|
+
createHeader(buf) {
|
|
458
|
+
if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
|
|
459
|
+
buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
|
|
460
|
+
}
|
|
461
|
+
const h = new Uint8Array(buf);
|
|
462
|
+
h.set(HEADER_TO_DEVICE);
|
|
463
|
+
return h;
|
|
464
|
+
}
|
|
465
|
+
cdcCommand(cmd) {
|
|
466
|
+
const h = new Uint8Array(5);
|
|
467
|
+
h.set(HEADER_TO_DEVICE);
|
|
468
|
+
h[4] = cmd;
|
|
469
|
+
return h;
|
|
470
|
+
}
|
|
471
|
+
cdcCommandWithData(cmd, data) {
|
|
472
|
+
const h = new Uint8Array(6 + data.length);
|
|
473
|
+
h.set(HEADER_TO_DEVICE);
|
|
474
|
+
h[4] = cmd;
|
|
475
|
+
h[5] = data.length;
|
|
476
|
+
h.set(data, 6);
|
|
477
|
+
return h;
|
|
478
|
+
}
|
|
479
|
+
cdc2Command(cmd, ext) {
|
|
480
|
+
const h = new Uint8Array(9);
|
|
481
|
+
h.set(HEADER_TO_DEVICE);
|
|
482
|
+
h[4] = cmd;
|
|
483
|
+
h[5] = ext;
|
|
484
|
+
h[6] = 0;
|
|
485
|
+
this.appendCrc16(h);
|
|
486
|
+
return h;
|
|
487
|
+
}
|
|
488
|
+
cdc2CommandBufferLength(data) {
|
|
489
|
+
return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
|
|
490
|
+
}
|
|
491
|
+
cdc2CommandWithData(cmd, ext, data) {
|
|
492
|
+
const h = new Uint8Array(this.cdc2CommandBufferLength(data));
|
|
493
|
+
h.set(HEADER_TO_DEVICE);
|
|
494
|
+
h[4] = cmd;
|
|
495
|
+
h[5] = ext;
|
|
496
|
+
if (data.length < 128) {
|
|
497
|
+
h[6] = data.length;
|
|
498
|
+
h.set(data, 7);
|
|
499
|
+
} else {
|
|
500
|
+
h[6] = data.length >>> 8 | 128;
|
|
501
|
+
h[7] = data.length & 255;
|
|
502
|
+
h.set(data, 8);
|
|
503
|
+
}
|
|
504
|
+
this.appendCrc16(h);
|
|
505
|
+
return h;
|
|
506
|
+
}
|
|
507
|
+
appendCrc16(h) {
|
|
508
|
+
const crc = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
|
|
509
|
+
h[h.length - 2] = crc >>> 8;
|
|
510
|
+
h[h.length - 1] = crc & 255;
|
|
511
|
+
}
|
|
512
|
+
validateHeader(data) {
|
|
513
|
+
return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
|
|
514
|
+
}
|
|
515
|
+
validateMessageCdc(data) {
|
|
516
|
+
const crc = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
|
|
517
|
+
return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc;
|
|
518
|
+
}
|
|
519
|
+
getPayloadSize(data) {
|
|
520
|
+
const a = data[3];
|
|
521
|
+
return (a & 128) === 0 ? a : ((a & 127) << 8) + data[4];
|
|
522
|
+
}
|
|
523
|
+
getHostHeaderLength(data) {
|
|
524
|
+
return (data[3] & 128) === 0 ? 4 : 5;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
1201
527
|
|
|
1202
528
|
// src/VexFirmwareVersion.ts
|
|
1203
529
|
class VexFirmwareVersion {
|
|
@@ -1213,9 +539,8 @@ class VexFirmwareVersion {
|
|
|
1213
539
|
}
|
|
1214
540
|
static fromString(version) {
|
|
1215
541
|
const parts = version.toLowerCase().replace(/b/g, "").split(".").map((x) => parseInt(x, 10));
|
|
1216
|
-
while (parts.length < 4)
|
|
542
|
+
while (parts.length < 4)
|
|
1217
543
|
parts.push(0);
|
|
1218
|
-
}
|
|
1219
544
|
return new VexFirmwareVersion(parts[0], parts[1], parts[2], parts[3]);
|
|
1220
545
|
}
|
|
1221
546
|
static fromUint8Array(data, offset = 0, reverse = false) {
|
|
@@ -1245,24 +570,23 @@ class VexFirmwareVersion {
|
|
|
1245
570
|
return `${this.toUserString()}.b${this.beta}`;
|
|
1246
571
|
}
|
|
1247
572
|
compare(that) {
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
return buildComp;
|
|
1258
|
-
} else if (betaComp !== 0) {
|
|
1259
|
-
return betaComp;
|
|
573
|
+
for (const [a, b] of [
|
|
574
|
+
[this.major, that.major],
|
|
575
|
+
[this.minor, that.minor],
|
|
576
|
+
[this.build, that.build],
|
|
577
|
+
[this.beta, that.beta]
|
|
578
|
+
]) {
|
|
579
|
+
const delta = a - b;
|
|
580
|
+
if (delta !== 0)
|
|
581
|
+
return delta;
|
|
1260
582
|
}
|
|
1261
583
|
return 0;
|
|
1262
584
|
}
|
|
1263
585
|
}
|
|
1264
586
|
|
|
1265
587
|
// src/VexPacketView.ts
|
|
588
|
+
var textDecoder = new TextDecoder("UTF-8");
|
|
589
|
+
|
|
1266
590
|
class PacketView extends DataView {
|
|
1267
591
|
position = 0;
|
|
1268
592
|
littleEndianDefault = true;
|
|
@@ -1270,19 +594,15 @@ class PacketView extends DataView {
|
|
|
1270
594
|
super(buffer, offset, length);
|
|
1271
595
|
}
|
|
1272
596
|
static fromPacket(packet) {
|
|
1273
|
-
const view = new PacketView(packet.data.buffer, packet.data.byteOffset);
|
|
597
|
+
const view = new PacketView(packet.data.buffer, packet.data.byteOffset, packet.data.byteLength);
|
|
1274
598
|
view.position = packet.ackIndex + 1;
|
|
1275
599
|
return view;
|
|
1276
600
|
}
|
|
1277
601
|
nextInt8() {
|
|
1278
|
-
|
|
1279
|
-
this.position += 1;
|
|
1280
|
-
return result;
|
|
602
|
+
return this.getInt8(this.position++);
|
|
1281
603
|
}
|
|
1282
604
|
nextUint8() {
|
|
1283
|
-
|
|
1284
|
-
this.position += 1;
|
|
1285
|
-
return result;
|
|
605
|
+
return this.getUint8(this.position++);
|
|
1286
606
|
}
|
|
1287
607
|
nextInt16(littleEndian = this.littleEndianDefault) {
|
|
1288
608
|
const result = this.getInt16(this.position, littleEndian);
|
|
@@ -1305,37 +625,28 @@ class PacketView extends DataView {
|
|
|
1305
625
|
return result;
|
|
1306
626
|
}
|
|
1307
627
|
nextString(length) {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
result += String.fromCharCode(this.nextUint8());
|
|
1311
|
-
}
|
|
628
|
+
const result = textDecoder.decode(new Uint8Array(this.buffer, this.byteOffset + this.position, length));
|
|
629
|
+
this.position += length;
|
|
1312
630
|
return result;
|
|
1313
631
|
}
|
|
1314
632
|
nextNTBS(length) {
|
|
1315
|
-
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
if (this.byteLength <= this.position)
|
|
1319
|
-
break;
|
|
1320
|
-
const g = this.nextUint8();
|
|
1321
|
-
if (g === 0)
|
|
1322
|
-
break;
|
|
1323
|
-
result += String.fromCharCode(g);
|
|
1324
|
-
}
|
|
1325
|
-
this.position = lastPosition + length;
|
|
633
|
+
const start = this.position;
|
|
634
|
+
const result = this.nextVarNTBS(length);
|
|
635
|
+
this.position = start + length;
|
|
1326
636
|
return result;
|
|
1327
637
|
}
|
|
1328
638
|
nextVarNTBS(length) {
|
|
1329
|
-
|
|
639
|
+
const start = this.position;
|
|
640
|
+
let byteLength = 0;
|
|
1330
641
|
for (let i = 0;i < length; i++) {
|
|
1331
642
|
if (this.byteLength <= this.position)
|
|
1332
643
|
break;
|
|
1333
644
|
const g = this.nextUint8();
|
|
1334
645
|
if (g === 0)
|
|
1335
646
|
break;
|
|
1336
|
-
|
|
647
|
+
byteLength++;
|
|
1337
648
|
}
|
|
1338
|
-
return
|
|
649
|
+
return textDecoder.decode(new Uint8Array(this.buffer, this.byteOffset + start, byteLength));
|
|
1339
650
|
}
|
|
1340
651
|
nextVersion(reverse = false) {
|
|
1341
652
|
const result = VexFirmwareVersion.fromUint8Array(new Uint8Array(this.buffer, this.byteOffset, this.byteLength), this.position, reverse);
|
|
@@ -1345,43 +656,34 @@ class PacketView extends DataView {
|
|
|
1345
656
|
}
|
|
1346
657
|
|
|
1347
658
|
// src/VexPacketModels.ts
|
|
1348
|
-
var
|
|
659
|
+
var textEncoder2 = new TextEncoder;
|
|
660
|
+
function filePayload(a, b, fileName) {
|
|
661
|
+
const payload = new Uint8Array(26);
|
|
662
|
+
payload[0] = a;
|
|
663
|
+
payload[1] = b;
|
|
664
|
+
payload.set(encodeFixedText(fileName, "Filename", 24), 2);
|
|
665
|
+
return payload;
|
|
666
|
+
}
|
|
667
|
+
var clamp100 = (value) => value !== undefined && value > 100 ? 100 : value;
|
|
1349
668
|
|
|
1350
669
|
class Query1H2DPacket extends DeviceBoundPacket {
|
|
1351
670
|
static COMMAND_ID = 33;
|
|
1352
671
|
static COMMAND_EXTENDED_ID = undefined;
|
|
1353
|
-
constructor() {
|
|
1354
|
-
super(undefined);
|
|
1355
|
-
}
|
|
1356
672
|
}
|
|
1357
673
|
|
|
1358
674
|
class SystemVersionH2DPacket extends DeviceBoundPacket {
|
|
1359
675
|
static COMMAND_ID = 164;
|
|
1360
676
|
static COMMAND_EXTENDED_ID = undefined;
|
|
1361
|
-
constructor() {
|
|
1362
|
-
super(undefined);
|
|
1363
|
-
}
|
|
1364
677
|
}
|
|
1365
678
|
|
|
1366
679
|
class UpdateMatchModeH2DPacket extends DeviceBoundPacket {
|
|
1367
680
|
static COMMAND_ID = 88;
|
|
1368
681
|
static COMMAND_EXTENDED_ID = 193;
|
|
1369
682
|
constructor(mode, matchClock) {
|
|
1370
|
-
|
|
1371
|
-
switch (mode) {
|
|
1372
|
-
case "autonomous":
|
|
1373
|
-
bit1 = 10;
|
|
1374
|
-
break;
|
|
1375
|
-
case "driver":
|
|
1376
|
-
bit1 = 8;
|
|
1377
|
-
break;
|
|
1378
|
-
case "disabled":
|
|
1379
|
-
bit1 = 11;
|
|
1380
|
-
}
|
|
683
|
+
const bit1 = mode === "autonomous" ? 10 : mode === "driver" ? 8 : 11;
|
|
1381
684
|
const payload = new Uint8Array(5);
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
view.setUint32(1, matchClock, true);
|
|
685
|
+
payload[0] = bit1 & 15;
|
|
686
|
+
new DataView(payload.buffer).setUint32(1, matchClock, true);
|
|
1385
687
|
super(payload);
|
|
1386
688
|
}
|
|
1387
689
|
}
|
|
@@ -1389,18 +691,13 @@ class UpdateMatchModeH2DPacket extends DeviceBoundPacket {
|
|
|
1389
691
|
class GetMatchStatusH2DPacket extends DeviceBoundPacket {
|
|
1390
692
|
static COMMAND_ID = 88;
|
|
1391
693
|
static COMMAND_EXTENDED_ID = 194;
|
|
1392
|
-
constructor() {
|
|
1393
|
-
super(undefined);
|
|
1394
|
-
}
|
|
1395
694
|
}
|
|
1396
695
|
|
|
1397
696
|
class GetRadioModeH2DPacket extends DeviceBoundPacket {
|
|
1398
697
|
static COMMAND_ID = 88;
|
|
1399
698
|
static COMMAND_EXTENDED_ID = 65;
|
|
1400
699
|
constructor(mode) {
|
|
1401
|
-
|
|
1402
|
-
payload[0] = mode;
|
|
1403
|
-
super(payload);
|
|
700
|
+
super(Uint8Array.of(mode));
|
|
1404
701
|
}
|
|
1405
702
|
}
|
|
1406
703
|
|
|
@@ -1408,9 +705,7 @@ class FileControlH2DPacket extends DeviceBoundPacket {
|
|
|
1408
705
|
static COMMAND_ID = 86;
|
|
1409
706
|
static COMMAND_EXTENDED_ID = 16;
|
|
1410
707
|
constructor(a, b) {
|
|
1411
|
-
|
|
1412
|
-
payload.set([a, b], 0);
|
|
1413
|
-
super(payload);
|
|
708
|
+
super(Uint8Array.of(a, b));
|
|
1414
709
|
}
|
|
1415
710
|
}
|
|
1416
711
|
|
|
@@ -1420,25 +715,21 @@ class InitFileTransferH2DPacket extends DeviceBoundPacket {
|
|
|
1420
715
|
constructor(operation, target, vendor, options, binary, addr, name, type, version = new VexFirmwareVersion(1, 0, 0, 0)) {
|
|
1421
716
|
const payload = new Uint8Array(52);
|
|
1422
717
|
const view = new DataView(payload.buffer);
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
718
|
+
payload[0] = operation;
|
|
719
|
+
payload[1] = target;
|
|
720
|
+
payload[2] = vendor;
|
|
721
|
+
payload[3] = options;
|
|
1427
722
|
view.setUint32(4, binary.length, true);
|
|
1428
723
|
view.setUint32(8, addr, true);
|
|
1429
724
|
view.setUint32(12, operation === 1 /* WRITE */ ? Packet.ENCODER.crcgen.crc32(binary, 0) : 0, true);
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
ext ??= "";
|
|
1434
|
-
ext = ext === "gz" ? "bin" : ext;
|
|
725
|
+
let ext = /(?:\.([^.]+))?$/.exec(name)?.[1] ?? "";
|
|
726
|
+
if (ext === "gz")
|
|
727
|
+
ext = "bin";
|
|
1435
728
|
payload.set(encodeFixedText(type ?? ext, "File type", 4), 16);
|
|
1436
729
|
const timestamp = (Date.now() / 1000 >>> 0) - PacketEncoder.J2000_EPOCH;
|
|
1437
730
|
view.setUint32(20, timestamp, true);
|
|
1438
731
|
payload.set(version.toUint8Array(), 24);
|
|
1439
|
-
|
|
1440
|
-
payload.set(nameEncoded, 28);
|
|
1441
|
-
view.setUint8(51, 0);
|
|
732
|
+
payload.set(encodeFixedText(name, "Filename", 24), 28);
|
|
1442
733
|
super(payload);
|
|
1443
734
|
}
|
|
1444
735
|
}
|
|
@@ -1447,9 +738,7 @@ class ExitFileTransferH2DPacket extends DeviceBoundPacket {
|
|
|
1447
738
|
static COMMAND_ID = 86;
|
|
1448
739
|
static COMMAND_EXTENDED_ID = 18;
|
|
1449
740
|
constructor(action) {
|
|
1450
|
-
|
|
1451
|
-
payload[0] = action;
|
|
1452
|
-
super(payload);
|
|
741
|
+
super(Uint8Array.of(action));
|
|
1453
742
|
}
|
|
1454
743
|
}
|
|
1455
744
|
|
|
@@ -1458,8 +747,7 @@ class WriteFileH2DPacket extends DeviceBoundPacket {
|
|
|
1458
747
|
static COMMAND_EXTENDED_ID = 19;
|
|
1459
748
|
constructor(addr, buf) {
|
|
1460
749
|
const payload = new Uint8Array(4 + buf.length);
|
|
1461
|
-
|
|
1462
|
-
view.setUint32(0, addr, true);
|
|
750
|
+
new DataView(payload.buffer).setUint32(0, addr, true);
|
|
1463
751
|
payload.set(buf, 4);
|
|
1464
752
|
super(payload);
|
|
1465
753
|
}
|
|
@@ -1481,11 +769,7 @@ class LinkFileH2DPacket extends DeviceBoundPacket {
|
|
|
1481
769
|
static COMMAND_ID = 86;
|
|
1482
770
|
static COMMAND_EXTENDED_ID = 21;
|
|
1483
771
|
constructor(vendor, fileName, options) {
|
|
1484
|
-
|
|
1485
|
-
const payload = new Uint8Array(26);
|
|
1486
|
-
payload.set([vendor, options], 0);
|
|
1487
|
-
payload.set(str, 2);
|
|
1488
|
-
super(payload);
|
|
772
|
+
super(filePayload(vendor, options, fileName));
|
|
1489
773
|
}
|
|
1490
774
|
}
|
|
1491
775
|
|
|
@@ -1493,9 +777,7 @@ class GetDirectoryFileCountH2DPacket extends DeviceBoundPacket {
|
|
|
1493
777
|
static COMMAND_ID = 86;
|
|
1494
778
|
static COMMAND_EXTENDED_ID = 22;
|
|
1495
779
|
constructor(vendor) {
|
|
1496
|
-
|
|
1497
|
-
payload.set([vendor, 0], 0);
|
|
1498
|
-
super(payload);
|
|
780
|
+
super(Uint8Array.of(vendor, 0));
|
|
1499
781
|
}
|
|
1500
782
|
}
|
|
1501
783
|
|
|
@@ -1503,9 +785,7 @@ class GetDirectoryEntryH2DPacket extends DeviceBoundPacket {
|
|
|
1503
785
|
static COMMAND_ID = 86;
|
|
1504
786
|
static COMMAND_EXTENDED_ID = 23;
|
|
1505
787
|
constructor(index) {
|
|
1506
|
-
|
|
1507
|
-
payload.set([index, 0], 0);
|
|
1508
|
-
super(payload);
|
|
788
|
+
super(Uint8Array.of(index, 0));
|
|
1509
789
|
}
|
|
1510
790
|
}
|
|
1511
791
|
|
|
@@ -1513,17 +793,8 @@ class LoadFileActionH2DPacket extends DeviceBoundPacket {
|
|
|
1513
793
|
static COMMAND_ID = 86;
|
|
1514
794
|
static COMMAND_EXTENDED_ID = 24;
|
|
1515
795
|
constructor(vendor, actionId, fileNameOrSlotNumber) {
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
fileName = fileNameOrSlotNumber;
|
|
1519
|
-
} else {
|
|
1520
|
-
fileName = "___s_" + (fileNameOrSlotNumber - 1) + ".bin";
|
|
1521
|
-
}
|
|
1522
|
-
const encodedName = encodeFixedText(fileName, "Filename", 23);
|
|
1523
|
-
const payload = new Uint8Array(26);
|
|
1524
|
-
payload.set([vendor, actionId], 0);
|
|
1525
|
-
payload.set(encodedName, 2);
|
|
1526
|
-
super(payload);
|
|
796
|
+
const fileName = typeof fileNameOrSlotNumber === "string" ? fileNameOrSlotNumber : `___s_${fileNameOrSlotNumber - 1}.bin`;
|
|
797
|
+
super(filePayload(vendor, actionId, fileName));
|
|
1527
798
|
}
|
|
1528
799
|
}
|
|
1529
800
|
|
|
@@ -1531,11 +802,7 @@ class GetFileMetadataH2DPacket extends DeviceBoundPacket {
|
|
|
1531
802
|
static COMMAND_ID = 86;
|
|
1532
803
|
static COMMAND_EXTENDED_ID = 25;
|
|
1533
804
|
constructor(vendor, fileName, options) {
|
|
1534
|
-
|
|
1535
|
-
const payload = new Uint8Array(26);
|
|
1536
|
-
payload.set([vendor, options], 0);
|
|
1537
|
-
payload.set(encodedName, 2);
|
|
1538
|
-
super(payload);
|
|
805
|
+
super(filePayload(vendor, options, fileName));
|
|
1539
806
|
}
|
|
1540
807
|
}
|
|
1541
808
|
|
|
@@ -1543,11 +810,7 @@ class EraseFileH2DPacket extends DeviceBoundPacket {
|
|
|
1543
810
|
static COMMAND_ID = 86;
|
|
1544
811
|
static COMMAND_EXTENDED_ID = 27;
|
|
1545
812
|
constructor(vendor, fileName) {
|
|
1546
|
-
|
|
1547
|
-
const payload = new Uint8Array(26);
|
|
1548
|
-
payload.set([vendor, 128], 0);
|
|
1549
|
-
payload.set(encodedName, 2);
|
|
1550
|
-
super(payload);
|
|
813
|
+
super(filePayload(vendor, 128, fileName));
|
|
1551
814
|
}
|
|
1552
815
|
}
|
|
1553
816
|
|
|
@@ -1555,11 +818,7 @@ class GetProgramSlotInfoH2DPacket extends DeviceBoundPacket {
|
|
|
1555
818
|
static COMMAND_ID = 86;
|
|
1556
819
|
static COMMAND_EXTENDED_ID = 28;
|
|
1557
820
|
constructor(vendor, fileName) {
|
|
1558
|
-
|
|
1559
|
-
const payload = new Uint8Array(26);
|
|
1560
|
-
payload.set([vendor, 0], 0);
|
|
1561
|
-
payload.set(encodedName, 2);
|
|
1562
|
-
super(payload);
|
|
821
|
+
super(filePayload(vendor, 0, fileName));
|
|
1563
822
|
}
|
|
1564
823
|
}
|
|
1565
824
|
|
|
@@ -1567,9 +826,7 @@ class FileClearUpH2DPacket extends DeviceBoundPacket {
|
|
|
1567
826
|
static COMMAND_ID = 86;
|
|
1568
827
|
static COMMAND_EXTENDED_ID = 30;
|
|
1569
828
|
constructor(vendor) {
|
|
1570
|
-
|
|
1571
|
-
payload.set([vendor, 0], 0);
|
|
1572
|
-
super(payload);
|
|
829
|
+
super(Uint8Array.of(vendor, 0));
|
|
1573
830
|
}
|
|
1574
831
|
}
|
|
1575
832
|
|
|
@@ -1577,9 +834,7 @@ class FileFormatH2DPacket extends DeviceBoundPacket {
|
|
|
1577
834
|
static COMMAND_ID = 86;
|
|
1578
835
|
static COMMAND_EXTENDED_ID = 31;
|
|
1579
836
|
constructor() {
|
|
1580
|
-
|
|
1581
|
-
payload.set([68, 67, 66, 65], 0);
|
|
1582
|
-
super(payload);
|
|
837
|
+
super(Uint8Array.of(68, 67, 66, 65));
|
|
1583
838
|
}
|
|
1584
839
|
}
|
|
1585
840
|
|
|
@@ -1591,33 +846,21 @@ class GetSystemFlagsH2DPacket extends DeviceBoundPacket {
|
|
|
1591
846
|
class GetDeviceStatusH2DPacket extends DeviceBoundPacket {
|
|
1592
847
|
static COMMAND_ID = 86;
|
|
1593
848
|
static COMMAND_EXTENDED_ID = 33;
|
|
1594
|
-
constructor() {
|
|
1595
|
-
super(undefined);
|
|
1596
|
-
}
|
|
1597
849
|
}
|
|
1598
850
|
|
|
1599
851
|
class GetSystemStatusH2DPacket extends DeviceBoundPacket {
|
|
1600
852
|
static COMMAND_ID = 86;
|
|
1601
853
|
static COMMAND_EXTENDED_ID = 34;
|
|
1602
|
-
constructor() {
|
|
1603
|
-
super(undefined);
|
|
1604
|
-
}
|
|
1605
854
|
}
|
|
1606
855
|
|
|
1607
856
|
class GetFdtStatusH2DPacket extends DeviceBoundPacket {
|
|
1608
857
|
static COMMAND_ID = 86;
|
|
1609
858
|
static COMMAND_EXTENDED_ID = 35;
|
|
1610
|
-
constructor() {
|
|
1611
|
-
super(undefined);
|
|
1612
|
-
}
|
|
1613
859
|
}
|
|
1614
860
|
|
|
1615
861
|
class GetLogCountH2DPacket extends DeviceBoundPacket {
|
|
1616
862
|
static COMMAND_ID = 86;
|
|
1617
863
|
static COMMAND_EXTENDED_ID = 36;
|
|
1618
|
-
constructor() {
|
|
1619
|
-
super(undefined);
|
|
1620
|
-
}
|
|
1621
864
|
}
|
|
1622
865
|
|
|
1623
866
|
class ReadLogPageH2DPacket extends DeviceBoundPacket {
|
|
@@ -1635,18 +878,13 @@ class ReadLogPageH2DPacket extends DeviceBoundPacket {
|
|
|
1635
878
|
class GetRadioStatusH2DPacket extends DeviceBoundPacket {
|
|
1636
879
|
static COMMAND_ID = 86;
|
|
1637
880
|
static COMMAND_EXTENDED_ID = 38;
|
|
1638
|
-
constructor() {
|
|
1639
|
-
super(undefined);
|
|
1640
|
-
}
|
|
1641
881
|
}
|
|
1642
882
|
|
|
1643
883
|
class ScreenCaptureH2DPacket extends DeviceBoundPacket {
|
|
1644
884
|
static COMMAND_ID = 86;
|
|
1645
885
|
static COMMAND_EXTENDED_ID = 40;
|
|
1646
886
|
constructor(e) {
|
|
1647
|
-
|
|
1648
|
-
payload[0] = e;
|
|
1649
|
-
super(payload);
|
|
887
|
+
super(Uint8Array.of(e));
|
|
1650
888
|
}
|
|
1651
889
|
}
|
|
1652
890
|
|
|
@@ -1667,10 +905,7 @@ class SelectDashH2DPacket extends DeviceBoundPacket {
|
|
|
1667
905
|
static COMMAND_ID = 86;
|
|
1668
906
|
static COMMAND_EXTENDED_ID = 43;
|
|
1669
907
|
constructor(screen, port) {
|
|
1670
|
-
|
|
1671
|
-
payload[0] = screen;
|
|
1672
|
-
payload[1] = port;
|
|
1673
|
-
super(payload);
|
|
908
|
+
super(Uint8Array.of(screen, port));
|
|
1674
909
|
}
|
|
1675
910
|
}
|
|
1676
911
|
|
|
@@ -1678,9 +913,8 @@ class ReadKeyValueH2DPacket extends DeviceBoundPacket {
|
|
|
1678
913
|
static COMMAND_ID = 86;
|
|
1679
914
|
static COMMAND_EXTENDED_ID = 46;
|
|
1680
915
|
constructor(key) {
|
|
1681
|
-
const encodedKey = encodeFixedText(key, "Key", 31);
|
|
1682
916
|
const payload = new Uint8Array(32);
|
|
1683
|
-
payload.set(
|
|
917
|
+
payload.set(encodeFixedText(key, "Key", 31), 0);
|
|
1684
918
|
super(payload);
|
|
1685
919
|
}
|
|
1686
920
|
}
|
|
@@ -1690,7 +924,7 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
|
|
|
1690
924
|
static COMMAND_EXTENDED_ID = 47;
|
|
1691
925
|
constructor(key, value) {
|
|
1692
926
|
const strk = encodeFixedText(key, "Key", 31);
|
|
1693
|
-
const strv =
|
|
927
|
+
const strv = textEncoder2.encode(value);
|
|
1694
928
|
if (strk.byteLength + strv.byteLength + 20 > 32767) {
|
|
1695
929
|
throw new RangeError("Key and value are too large for a protocol packet");
|
|
1696
930
|
}
|
|
@@ -1704,17 +938,11 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
|
|
|
1704
938
|
class GetSlot1to4InfoH2DPacket extends DeviceBoundPacket {
|
|
1705
939
|
static COMMAND_ID = 86;
|
|
1706
940
|
static COMMAND_EXTENDED_ID = 49;
|
|
1707
|
-
constructor() {
|
|
1708
|
-
super(undefined);
|
|
1709
|
-
}
|
|
1710
941
|
}
|
|
1711
942
|
|
|
1712
943
|
class GetSlot5to8InfoH2DPacket extends DeviceBoundPacket {
|
|
1713
944
|
static COMMAND_ID = 86;
|
|
1714
945
|
static COMMAND_EXTENDED_ID = 50;
|
|
1715
|
-
constructor() {
|
|
1716
|
-
super(undefined);
|
|
1717
|
-
}
|
|
1718
946
|
}
|
|
1719
947
|
|
|
1720
948
|
class FactoryStatusH2DPacket extends DeviceBoundPacket {
|
|
@@ -1726,9 +954,7 @@ class FactoryEnableH2DPacket extends DeviceBoundPacket {
|
|
|
1726
954
|
static COMMAND_ID = 86;
|
|
1727
955
|
static COMMAND_EXTENDED_ID = 255;
|
|
1728
956
|
constructor() {
|
|
1729
|
-
|
|
1730
|
-
payload.set([77, 76, 75, 74], 0);
|
|
1731
|
-
super(payload);
|
|
957
|
+
super(Uint8Array.of(77, 76, 75, 74));
|
|
1732
958
|
}
|
|
1733
959
|
}
|
|
1734
960
|
|
|
@@ -1770,8 +996,7 @@ class MatchModeReplyD2HPacket extends HostBoundPacket {
|
|
|
1770
996
|
modebit;
|
|
1771
997
|
constructor(data) {
|
|
1772
998
|
super(data);
|
|
1773
|
-
|
|
1774
|
-
this.modebit = dataView.nextUint8();
|
|
999
|
+
this.modebit = PacketView.fromPacket(this).nextUint8();
|
|
1775
1000
|
}
|
|
1776
1001
|
}
|
|
1777
1002
|
|
|
@@ -1797,31 +1022,27 @@ class MatchStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
1797
1022
|
rxSignalQuality;
|
|
1798
1023
|
constructor(data) {
|
|
1799
1024
|
super(data);
|
|
1800
|
-
const
|
|
1025
|
+
const view = PacketView.fromPacket(this);
|
|
1801
1026
|
const n = this.ackIndex;
|
|
1802
|
-
this.rssi =
|
|
1803
|
-
this.systemStatusBits =
|
|
1804
|
-
this.radioStatusBits =
|
|
1805
|
-
this.fieldStatusBits =
|
|
1806
|
-
this.matchClock =
|
|
1807
|
-
this.brainBatteryPercent =
|
|
1808
|
-
this.controllerBatteryPercent =
|
|
1809
|
-
this.partnerControllerBatteryPercent =
|
|
1810
|
-
this.pad =
|
|
1811
|
-
this.buttons =
|
|
1812
|
-
this.activeProgram =
|
|
1813
|
-
this.radioType =
|
|
1814
|
-
this.radioChannel =
|
|
1815
|
-
this.radioSlot =
|
|
1816
|
-
this.
|
|
1817
|
-
this.
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
if (endIdx > -1) {
|
|
1822
|
-
rawStr = rawStr.substr(0, endIdx);
|
|
1823
|
-
}
|
|
1824
|
-
this.robotName = rawStr;
|
|
1027
|
+
this.rssi = view.nextInt8();
|
|
1028
|
+
this.systemStatusBits = view.nextUint16();
|
|
1029
|
+
this.radioStatusBits = view.nextUint16();
|
|
1030
|
+
this.fieldStatusBits = view.nextUint8();
|
|
1031
|
+
this.matchClock = view.nextUint8();
|
|
1032
|
+
this.brainBatteryPercent = view.nextUint8();
|
|
1033
|
+
this.controllerBatteryPercent = view.nextUint8();
|
|
1034
|
+
this.partnerControllerBatteryPercent = view.nextUint8();
|
|
1035
|
+
this.pad = view.nextUint8();
|
|
1036
|
+
this.buttons = view.nextUint16();
|
|
1037
|
+
this.activeProgram = view.nextUint8();
|
|
1038
|
+
this.radioType = view.nextUint8();
|
|
1039
|
+
this.radioChannel = view.nextUint8();
|
|
1040
|
+
this.radioSlot = view.nextUint8();
|
|
1041
|
+
this.controllerFlags = view.getUint8(n + 28);
|
|
1042
|
+
this.rxSignalQuality = view.getUint8(n + 29);
|
|
1043
|
+
const raw = new TextDecoder("UTF-8").decode(this.data.slice(n + 18, n + this.payloadSize + 28));
|
|
1044
|
+
const end = raw.indexOf("\x00");
|
|
1045
|
+
this.robotName = end > -1 ? raw.slice(0, end) : raw;
|
|
1825
1046
|
}
|
|
1826
1047
|
}
|
|
1827
1048
|
|
|
@@ -1838,10 +1059,10 @@ class InitFileTransferReplyD2HPacket extends HostBoundPacket {
|
|
|
1838
1059
|
crc32;
|
|
1839
1060
|
constructor(data) {
|
|
1840
1061
|
super(data);
|
|
1841
|
-
const
|
|
1842
|
-
this.windowSize =
|
|
1843
|
-
this.fileSize =
|
|
1844
|
-
this.crc32 =
|
|
1062
|
+
const view = PacketView.fromPacket(this);
|
|
1063
|
+
this.windowSize = view.nextUint16();
|
|
1064
|
+
this.fileSize = view.nextUint32();
|
|
1065
|
+
this.crc32 = view.nextUint32();
|
|
1845
1066
|
}
|
|
1846
1067
|
}
|
|
1847
1068
|
|
|
@@ -1863,13 +1084,10 @@ class ReadFileReplyD2HPacket extends HostBoundPacket {
|
|
|
1863
1084
|
buf;
|
|
1864
1085
|
constructor(data) {
|
|
1865
1086
|
super(data);
|
|
1866
|
-
const
|
|
1867
|
-
this.addr =
|
|
1087
|
+
const view = PacketView.fromPacket(this);
|
|
1088
|
+
this.addr = view.nextUint32();
|
|
1868
1089
|
this.length = this.payloadSize - 8;
|
|
1869
|
-
this.buf = this.data.
|
|
1870
|
-
}
|
|
1871
|
-
static isValidPacket(data, n) {
|
|
1872
|
-
return super.isValidPacket(data, n);
|
|
1090
|
+
this.buf = this.data.subarray(view.position, view.position + this.length);
|
|
1873
1091
|
}
|
|
1874
1092
|
}
|
|
1875
1093
|
|
|
@@ -1884,8 +1102,7 @@ class GetDirectoryFileCountReplyD2HPacket extends HostBoundPacket {
|
|
|
1884
1102
|
count;
|
|
1885
1103
|
constructor(data) {
|
|
1886
1104
|
super(data);
|
|
1887
|
-
|
|
1888
|
-
this.count = dataView.nextUint16();
|
|
1105
|
+
this.count = PacketView.fromPacket(this).nextUint16();
|
|
1889
1106
|
}
|
|
1890
1107
|
}
|
|
1891
1108
|
|
|
@@ -1895,19 +1112,19 @@ class GetDirectoryEntryReplyD2HPacket extends HostBoundPacket {
|
|
|
1895
1112
|
file;
|
|
1896
1113
|
constructor(data) {
|
|
1897
1114
|
super(data);
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
}
|
|
1115
|
+
if (this.payloadSize <= 4)
|
|
1116
|
+
return;
|
|
1117
|
+
const view = PacketView.fromPacket(this);
|
|
1118
|
+
this.file = {
|
|
1119
|
+
index: view.nextUint8(),
|
|
1120
|
+
size: view.nextUint32(),
|
|
1121
|
+
loadAddress: view.nextUint32(),
|
|
1122
|
+
crc32: view.nextUint32(),
|
|
1123
|
+
type: view.nextString(4),
|
|
1124
|
+
timestamp: view.nextUint32() + PacketEncoder.J2000_EPOCH,
|
|
1125
|
+
version: view.nextVersion(),
|
|
1126
|
+
filename: view.nextNTBS(32)
|
|
1127
|
+
};
|
|
1911
1128
|
}
|
|
1912
1129
|
}
|
|
1913
1130
|
|
|
@@ -1922,18 +1139,18 @@ class GetFileMetadataReplyD2HPacket extends HostBoundPacket {
|
|
|
1922
1139
|
file;
|
|
1923
1140
|
constructor(data) {
|
|
1924
1141
|
super(data);
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
}
|
|
1142
|
+
if (this.payloadSize <= 4)
|
|
1143
|
+
return;
|
|
1144
|
+
const view = PacketView.fromPacket(this);
|
|
1145
|
+
view.nextUint8();
|
|
1146
|
+
this.file = {
|
|
1147
|
+
size: view.nextUint32(),
|
|
1148
|
+
loadAddress: view.nextUint32(),
|
|
1149
|
+
crc32: view.nextUint32(),
|
|
1150
|
+
type: view.nextString(4),
|
|
1151
|
+
timestamp: view.nextUint32() + PacketEncoder.J2000_EPOCH,
|
|
1152
|
+
version: view.nextVersion()
|
|
1153
|
+
};
|
|
1937
1154
|
}
|
|
1938
1155
|
}
|
|
1939
1156
|
|
|
@@ -1949,9 +1166,9 @@ class GetProgramSlotInfoReplyD2HPacket extends HostBoundPacket {
|
|
|
1949
1166
|
slot;
|
|
1950
1167
|
constructor(data) {
|
|
1951
1168
|
super(data);
|
|
1952
|
-
const
|
|
1953
|
-
this.slot =
|
|
1954
|
-
this.requestedSlot =
|
|
1169
|
+
const view = PacketView.fromPacket(this);
|
|
1170
|
+
this.slot = view.nextUint8();
|
|
1171
|
+
this.requestedSlot = view.nextUint8();
|
|
1955
1172
|
}
|
|
1956
1173
|
}
|
|
1957
1174
|
|
|
@@ -1969,40 +1186,32 @@ class GetSystemFlagsReplyD2HPacket extends HostBoundPacket {
|
|
|
1969
1186
|
static COMMAND_ID = 86;
|
|
1970
1187
|
static COMMAND_EXTENDED_ID = 32;
|
|
1971
1188
|
flags;
|
|
1972
|
-
radioSearching;
|
|
1189
|
+
radioSearching = false;
|
|
1973
1190
|
radioQuality;
|
|
1974
1191
|
controllerBatteryPercent;
|
|
1975
1192
|
partnerControllerBatteryPercent;
|
|
1976
1193
|
battery;
|
|
1977
|
-
currentProgram;
|
|
1194
|
+
currentProgram = 0;
|
|
1978
1195
|
constructor(data) {
|
|
1979
1196
|
super(data);
|
|
1980
|
-
const
|
|
1981
|
-
this.
|
|
1982
|
-
this.currentProgram = 0;
|
|
1983
|
-
this.flags = dataView.nextUint32();
|
|
1197
|
+
const view = PacketView.fromPacket(this);
|
|
1198
|
+
this.flags = view.nextUint32();
|
|
1984
1199
|
const hasPartner = (8192 & this.flags) !== 0;
|
|
1985
1200
|
const hasRadio = (1536 & this.flags) === 1536;
|
|
1986
|
-
const byte1 =
|
|
1987
|
-
const byte2 =
|
|
1201
|
+
const byte1 = view.nextUint8();
|
|
1202
|
+
const byte2 = view.nextUint8();
|
|
1988
1203
|
if (this.payloadSize === 11) {
|
|
1989
|
-
this.battery = 8 * (byte1 & 15);
|
|
1990
|
-
if ((this.flags & 256) !== 0 || hasRadio)
|
|
1991
|
-
this.controllerBatteryPercent = 8 * (byte1 >> 4 & 15);
|
|
1204
|
+
this.battery = clamp100(8 * (byte1 & 15));
|
|
1205
|
+
if ((this.flags & 256) !== 0 || hasRadio) {
|
|
1206
|
+
this.controllerBatteryPercent = clamp100(8 * (byte1 >> 4 & 15));
|
|
1207
|
+
}
|
|
1992
1208
|
if (hasRadio)
|
|
1993
|
-
this.radioQuality = 8 * (byte2 & 15);
|
|
1209
|
+
this.radioQuality = clamp100(8 * (byte2 & 15));
|
|
1994
1210
|
this.radioSearching = (this.flags & 1536) === 512;
|
|
1995
|
-
if (hasPartner)
|
|
1996
|
-
this.partnerControllerBatteryPercent = 8 * (byte2 >> 4 & 15);
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
this.battery = 100;
|
|
2000
|
-
if (this.controllerBatteryPercent != null && this.controllerBatteryPercent > 100)
|
|
2001
|
-
this.controllerBatteryPercent = 100;
|
|
2002
|
-
if (this.radioQuality != null && this.radioQuality > 100)
|
|
2003
|
-
this.radioQuality = 100;
|
|
2004
|
-
if (this.partnerControllerBatteryPercent != null && this.partnerControllerBatteryPercent > 100)
|
|
2005
|
-
this.partnerControllerBatteryPercent = 100;
|
|
1211
|
+
if (hasPartner) {
|
|
1212
|
+
this.partnerControllerBatteryPercent = clamp100(8 * (byte2 >> 4 & 15));
|
|
1213
|
+
}
|
|
1214
|
+
this.currentProgram = view.nextUint8();
|
|
2006
1215
|
}
|
|
2007
1216
|
}
|
|
2008
1217
|
}
|
|
@@ -2014,17 +1223,17 @@ class GetDeviceStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
2014
1223
|
devices;
|
|
2015
1224
|
constructor(data) {
|
|
2016
1225
|
super(data);
|
|
2017
|
-
const
|
|
2018
|
-
this.count =
|
|
1226
|
+
const view = PacketView.fromPacket(this);
|
|
1227
|
+
this.count = view.nextUint8();
|
|
2019
1228
|
this.devices = [];
|
|
2020
1229
|
for (let i = 0;i < this.count; i++) {
|
|
2021
1230
|
this.devices.push({
|
|
2022
|
-
port:
|
|
2023
|
-
type:
|
|
2024
|
-
status:
|
|
2025
|
-
betaversion:
|
|
2026
|
-
version:
|
|
2027
|
-
bootversion:
|
|
1231
|
+
port: view.nextUint8(),
|
|
1232
|
+
type: view.nextUint8(),
|
|
1233
|
+
status: view.nextUint8(),
|
|
1234
|
+
betaversion: view.nextUint8(),
|
|
1235
|
+
version: view.nextUint16(),
|
|
1236
|
+
bootversion: view.nextUint16()
|
|
2028
1237
|
});
|
|
2029
1238
|
}
|
|
2030
1239
|
}
|
|
@@ -2036,48 +1245,42 @@ class GetSystemStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
2036
1245
|
systemVersion;
|
|
2037
1246
|
cpu0Version;
|
|
2038
1247
|
cpu1Version;
|
|
2039
|
-
nxpVersion;
|
|
1248
|
+
nxpVersion = VexFirmwareVersion.allZero();
|
|
2040
1249
|
touchVersion;
|
|
2041
|
-
uniqueId;
|
|
2042
|
-
sysflags;
|
|
2043
|
-
eventBrain;
|
|
2044
|
-
romBootloaderActive;
|
|
2045
|
-
ramBootloaderActive;
|
|
2046
|
-
goldenVersion;
|
|
1250
|
+
uniqueId = 1234;
|
|
1251
|
+
sysflags = [0, 0, 0, 0, 0, 0, 0];
|
|
1252
|
+
eventBrain = false;
|
|
1253
|
+
romBootloaderActive = false;
|
|
1254
|
+
ramBootloaderActive = false;
|
|
1255
|
+
goldenVersion = VexFirmwareVersion.allZero();
|
|
2047
1256
|
constructor(data) {
|
|
2048
1257
|
super(data);
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
this.systemVersion =
|
|
2052
|
-
this.cpu0Version =
|
|
2053
|
-
this.cpu1Version =
|
|
2054
|
-
this.touchVersion =
|
|
2055
|
-
this.uniqueId = 1234;
|
|
2056
|
-
this.sysflags = [0, 0, 0, 0, 0, 0, 0];
|
|
2057
|
-
this.goldenVersion = VexFirmwareVersion.allZero();
|
|
2058
|
-
this.nxpVersion = VexFirmwareVersion.allZero();
|
|
2059
|
-
this.eventBrain = false;
|
|
2060
|
-
this.romBootloaderActive = false;
|
|
2061
|
-
this.ramBootloaderActive = false;
|
|
1258
|
+
const view = PacketView.fromPacket(this);
|
|
1259
|
+
view.nextUint8();
|
|
1260
|
+
this.systemVersion = view.nextVersion();
|
|
1261
|
+
this.cpu0Version = view.nextVersion();
|
|
1262
|
+
this.cpu1Version = view.nextVersion();
|
|
1263
|
+
this.touchVersion = view.nextVersion(true);
|
|
2062
1264
|
if (this.payloadSize > 25) {
|
|
2063
|
-
this.uniqueId =
|
|
1265
|
+
this.uniqueId = view.nextUint32();
|
|
2064
1266
|
this.sysflags = [
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1267
|
+
view.nextUint8(),
|
|
1268
|
+
view.nextUint8(),
|
|
1269
|
+
view.nextUint8(),
|
|
1270
|
+
view.nextUint8(),
|
|
1271
|
+
view.nextUint8(),
|
|
2070
1272
|
0,
|
|
2071
|
-
|
|
1273
|
+
view.nextUint8()
|
|
2072
1274
|
];
|
|
2073
|
-
|
|
2074
|
-
this.
|
|
2075
|
-
this.
|
|
2076
|
-
|
|
2077
|
-
|
|
1275
|
+
const flags6 = this.sysflags[6];
|
|
1276
|
+
this.eventBrain = (1 & flags6) !== 0;
|
|
1277
|
+
this.romBootloaderActive = (2 & flags6) !== 0;
|
|
1278
|
+
this.ramBootloaderActive = (4 & flags6) !== 0;
|
|
1279
|
+
view.nextUint16();
|
|
1280
|
+
this.goldenVersion = view.nextVersion();
|
|
2078
1281
|
}
|
|
2079
1282
|
if (this.payloadSize > 37) {
|
|
2080
|
-
this.nxpVersion =
|
|
1283
|
+
this.nxpVersion = view.nextVersion();
|
|
2081
1284
|
}
|
|
2082
1285
|
}
|
|
2083
1286
|
}
|
|
@@ -2089,17 +1292,17 @@ class GetFdtStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
2089
1292
|
status;
|
|
2090
1293
|
constructor(data) {
|
|
2091
1294
|
super(data);
|
|
2092
|
-
const
|
|
2093
|
-
this.count =
|
|
1295
|
+
const view = PacketView.fromPacket(this);
|
|
1296
|
+
this.count = view.nextUint8();
|
|
2094
1297
|
this.status = [];
|
|
2095
1298
|
for (let i = 0;i < this.count; i++) {
|
|
2096
1299
|
this.status.push({
|
|
2097
|
-
index:
|
|
2098
|
-
type:
|
|
2099
|
-
status:
|
|
2100
|
-
betaversion:
|
|
2101
|
-
version:
|
|
2102
|
-
bootversion:
|
|
1300
|
+
index: view.nextUint8(),
|
|
1301
|
+
type: view.nextUint8(),
|
|
1302
|
+
status: view.nextUint8(),
|
|
1303
|
+
betaversion: view.nextUint8(),
|
|
1304
|
+
version: view.nextUint16(),
|
|
1305
|
+
bootversion: view.nextUint16()
|
|
2103
1306
|
});
|
|
2104
1307
|
}
|
|
2105
1308
|
}
|
|
@@ -2111,9 +1314,9 @@ class GetLogCountReplyD2HPacket extends HostBoundPacket {
|
|
|
2111
1314
|
count;
|
|
2112
1315
|
constructor(data) {
|
|
2113
1316
|
super(data);
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
this.count =
|
|
1317
|
+
const view = PacketView.fromPacket(this);
|
|
1318
|
+
view.nextUint8();
|
|
1319
|
+
this.count = view.nextUint32();
|
|
2117
1320
|
}
|
|
2118
1321
|
}
|
|
2119
1322
|
|
|
@@ -2125,20 +1328,19 @@ class ReadLogPageReplyD2HPacket extends HostBoundPacket {
|
|
|
2125
1328
|
entries;
|
|
2126
1329
|
constructor(data) {
|
|
2127
1330
|
super(data);
|
|
2128
|
-
const
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
this.
|
|
2132
|
-
this.count = dataView.nextUint16();
|
|
1331
|
+
const view = PacketView.fromPacket(this);
|
|
1332
|
+
const size = view.nextUint8();
|
|
1333
|
+
this.offset = view.nextUint32();
|
|
1334
|
+
this.count = view.nextUint16();
|
|
2133
1335
|
this.entries = [];
|
|
2134
|
-
let j =
|
|
1336
|
+
let j = this.ackIndex + 8;
|
|
2135
1337
|
for (let i = 0;i < this.count; i++) {
|
|
2136
1338
|
this.entries.push({
|
|
2137
|
-
code:
|
|
2138
|
-
type:
|
|
2139
|
-
desc:
|
|
2140
|
-
spare:
|
|
2141
|
-
time:
|
|
1339
|
+
code: view.getUint8(j),
|
|
1340
|
+
type: view.getUint8(j + 1),
|
|
1341
|
+
desc: view.getUint8(j + 2),
|
|
1342
|
+
spare: view.getUint8(j + 3),
|
|
1343
|
+
time: view.getUint32(j + 4, true)
|
|
2142
1344
|
});
|
|
2143
1345
|
j += size;
|
|
2144
1346
|
}
|
|
@@ -2155,13 +1357,12 @@ class GetRadioStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
2155
1357
|
timeslot;
|
|
2156
1358
|
constructor(data) {
|
|
2157
1359
|
super(data);
|
|
2158
|
-
const
|
|
2159
|
-
|
|
2160
|
-
this.
|
|
2161
|
-
this.
|
|
2162
|
-
this.
|
|
2163
|
-
this.
|
|
2164
|
-
this.timeslot = this.data[n + 7];
|
|
1360
|
+
const view = PacketView.fromPacket(this);
|
|
1361
|
+
this.device = view.nextUint8();
|
|
1362
|
+
this.quality = view.nextUint16();
|
|
1363
|
+
this.strength = view.nextInt16();
|
|
1364
|
+
this.channel = this.data[this.ackIndex + 6];
|
|
1365
|
+
this.timeslot = this.data[this.ackIndex + 7];
|
|
2165
1366
|
}
|
|
2166
1367
|
}
|
|
2167
1368
|
|
|
@@ -2186,8 +1387,7 @@ class ReadKeyValueReplyD2HPacket extends HostBoundPacket {
|
|
|
2186
1387
|
value;
|
|
2187
1388
|
constructor(data) {
|
|
2188
1389
|
super(data);
|
|
2189
|
-
|
|
2190
|
-
this.value = dataView.nextVarNTBS(255);
|
|
1390
|
+
this.value = PacketView.fromPacket(this).nextVarNTBS(255);
|
|
2191
1391
|
}
|
|
2192
1392
|
}
|
|
2193
1393
|
|
|
@@ -2203,20 +1403,18 @@ class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
|
|
|
2203
1403
|
slots;
|
|
2204
1404
|
constructor(data, start = 1) {
|
|
2205
1405
|
super(data);
|
|
2206
|
-
const
|
|
2207
|
-
this.slotFlags =
|
|
1406
|
+
const view = PacketView.fromPacket(this);
|
|
1407
|
+
this.slotFlags = view.nextUint8();
|
|
2208
1408
|
this.slots = [];
|
|
2209
1409
|
for (let i = 0;i < 4; i++) {
|
|
2210
|
-
|
|
2211
|
-
if (!hasData)
|
|
1410
|
+
if ((this.slotFlags & 1 << start - 1 + i) === 0)
|
|
2212
1411
|
continue;
|
|
2213
|
-
const
|
|
2214
|
-
const nameLen =
|
|
2215
|
-
const name = dataView.nextString(nameLen);
|
|
1412
|
+
const icon = view.nextUint16();
|
|
1413
|
+
const nameLen = view.nextUint8();
|
|
2216
1414
|
this.slots.push({
|
|
2217
1415
|
slot: start + i,
|
|
2218
|
-
icon
|
|
2219
|
-
name
|
|
1416
|
+
icon,
|
|
1417
|
+
name: view.nextString(nameLen)
|
|
2220
1418
|
});
|
|
2221
1419
|
}
|
|
2222
1420
|
}
|
|
@@ -2238,9 +1436,9 @@ class FactoryStatusReplyD2HPacket extends HostBoundPacket {
|
|
|
2238
1436
|
percent;
|
|
2239
1437
|
constructor(data) {
|
|
2240
1438
|
super(data);
|
|
2241
|
-
const
|
|
2242
|
-
this.status =
|
|
2243
|
-
this.percent =
|
|
1439
|
+
const view = PacketView.fromPacket(this);
|
|
1440
|
+
this.status = view.nextUint8();
|
|
1441
|
+
this.percent = view.nextUint8();
|
|
2244
1442
|
}
|
|
2245
1443
|
}
|
|
2246
1444
|
|
|
@@ -2249,120 +1447,35 @@ class FactoryEnableReplyD2HPacket extends HostBoundPacket {
|
|
|
2249
1447
|
static COMMAND_EXTENDED_ID = 255;
|
|
2250
1448
|
}
|
|
2251
1449
|
|
|
2252
|
-
// src/
|
|
2253
|
-
var
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
class PacketEncoder {
|
|
2263
|
-
static HEADERS_LENGTH = 4;
|
|
2264
|
-
static HEADER_TO_DEVICE = [201, 54, 184, 71];
|
|
2265
|
-
static HEADER_TO_HOST = [170, 85];
|
|
2266
|
-
static J2000_EPOCH = 946684800;
|
|
2267
|
-
vexVersion;
|
|
2268
|
-
crcgen;
|
|
2269
|
-
allPacketsTable = {};
|
|
2270
|
-
static getInstance() {
|
|
2271
|
-
if (Packet.ENCODER === undefined) {
|
|
2272
|
-
Packet.ENCODER = new PacketEncoder;
|
|
2273
|
-
}
|
|
2274
|
-
return Packet.ENCODER;
|
|
2275
|
-
}
|
|
2276
|
-
constructor() {
|
|
2277
|
-
this.vexVersion = 0;
|
|
2278
|
-
this.crcgen = new CrcGenerator;
|
|
2279
|
-
Object.values(exports_VexPacketModels).forEach((packet) => {
|
|
2280
|
-
if (typeof packet === "function" && packet.prototype instanceof HostBoundPacket) {
|
|
2281
|
-
const packetType = packet;
|
|
2282
|
-
this.allPacketsTable[packetType.COMMAND_ID + " " + packetType.COMMAND_EXTENDED_ID] = packetType;
|
|
2283
|
-
}
|
|
2284
|
-
});
|
|
2285
|
-
}
|
|
2286
|
-
createHeader(buf) {
|
|
2287
|
-
if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
|
|
2288
|
-
buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
|
|
2289
|
-
}
|
|
2290
|
-
const h = new Uint8Array(buf);
|
|
2291
|
-
h.set(PacketEncoder.HEADER_TO_DEVICE);
|
|
2292
|
-
return h;
|
|
2293
|
-
}
|
|
2294
|
-
cdcCommand(cmd) {
|
|
2295
|
-
const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 1);
|
|
2296
|
-
const h = this.createHeader(buf);
|
|
2297
|
-
h.set([cmd], PacketEncoder.HEADERS_LENGTH);
|
|
2298
|
-
return h;
|
|
2299
|
-
}
|
|
2300
|
-
cdcCommandWithData(cmd, data) {
|
|
2301
|
-
const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 2 + data.length);
|
|
2302
|
-
const h = this.createHeader(buf);
|
|
2303
|
-
h.set([cmd, data.length], PacketEncoder.HEADERS_LENGTH);
|
|
2304
|
-
h.set(data, PacketEncoder.HEADERS_LENGTH + 2);
|
|
2305
|
-
return h;
|
|
2306
|
-
}
|
|
2307
|
-
cdc2Command(cmd, ext) {
|
|
2308
|
-
const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 5);
|
|
2309
|
-
const h = this.createHeader(buf);
|
|
2310
|
-
h.set([cmd, ext, 0], PacketEncoder.HEADERS_LENGTH);
|
|
2311
|
-
const crc = this.crcgen.crc16(h.subarray(0, buf.byteLength - 2), 0) >>> 0;
|
|
2312
|
-
h.set([crc >>> 8, crc & 255], buf.byteLength - 2);
|
|
2313
|
-
return h;
|
|
2314
|
-
}
|
|
2315
|
-
cdc2CommandBufferLength(data) {
|
|
2316
|
-
let length = PacketEncoder.HEADERS_LENGTH + data.length + 3 + 2;
|
|
2317
|
-
if (data.length > 127)
|
|
2318
|
-
length += 1;
|
|
2319
|
-
return length;
|
|
2320
|
-
}
|
|
2321
|
-
cdc2CommandWithData(cmd, ext, data) {
|
|
2322
|
-
const buf = new ArrayBuffer(this.cdc2CommandBufferLength(data));
|
|
2323
|
-
const h = this.createHeader(buf);
|
|
2324
|
-
if (data.length < 128) {
|
|
2325
|
-
h.set([cmd, ext, data.length], PacketEncoder.HEADERS_LENGTH);
|
|
2326
|
-
h.set(data, PacketEncoder.HEADERS_LENGTH + 3);
|
|
2327
|
-
} else {
|
|
2328
|
-
const lengthMsb = (data.length >>> 8 | 128) >>> 0;
|
|
2329
|
-
const lengthLsb = (data.length & 255) >>> 0;
|
|
2330
|
-
h.set([cmd, ext, lengthMsb, lengthLsb], PacketEncoder.HEADERS_LENGTH);
|
|
2331
|
-
h.set(data, PacketEncoder.HEADERS_LENGTH + 4);
|
|
2332
|
-
}
|
|
2333
|
-
const crc = this.crcgen.crc16(h.subarray(0, buf.byteLength - 2), 0);
|
|
2334
|
-
h.set([crc >>> 8, crc & 255], buf.byteLength - 2);
|
|
2335
|
-
return h;
|
|
2336
|
-
}
|
|
2337
|
-
validateHeader(data) {
|
|
2338
|
-
return !(data[0] !== PacketEncoder.HEADER_TO_HOST[0] || data[1] !== PacketEncoder.HEADER_TO_HOST[1]);
|
|
2339
|
-
}
|
|
2340
|
-
validateMessageCdc(data) {
|
|
2341
|
-
const message = data.subarray(0, data.byteLength - 2);
|
|
2342
|
-
const lastTwoBytes = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
|
|
2343
|
-
return this.crcgen.crc16(message, 0) === lastTwoBytes;
|
|
1450
|
+
// src/VexScreenCapture.ts
|
|
1451
|
+
var SCREEN_CAPTURE_HEIGHT = 272;
|
|
1452
|
+
var SCREEN_CAPTURE_WIDTH = 480;
|
|
1453
|
+
var SCREEN_CAPTURE_CHANNELS = 3;
|
|
1454
|
+
var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
|
|
1455
|
+
var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
|
|
1456
|
+
var SCREEN_CAPTURE_FRAMEBUFFER_SIZE = SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
1457
|
+
function convertScreenCapture(framebuffer) {
|
|
1458
|
+
if (framebuffer.length !== SCREEN_CAPTURE_FRAMEBUFFER_SIZE) {
|
|
1459
|
+
throw new Error(`bad screen capture framebuffer size: ${framebuffer.length}; expected ${SCREEN_CAPTURE_FRAMEBUFFER_SIZE}`);
|
|
2344
1460
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
1461
|
+
const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
|
|
1462
|
+
let source = 0;
|
|
1463
|
+
let target = 0;
|
|
1464
|
+
for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
|
|
1465
|
+
for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
|
|
1466
|
+
pixels[target] = framebuffer[source + 2];
|
|
1467
|
+
pixels[target + 1] = framebuffer[source + 1];
|
|
1468
|
+
pixels[target + 2] = framebuffer[source];
|
|
1469
|
+
source += SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
1470
|
+
target += SCREEN_CAPTURE_CHANNELS;
|
|
2351
1471
|
}
|
|
2352
|
-
|
|
2353
|
-
}
|
|
2354
|
-
getHostHeaderLength(data) {
|
|
2355
|
-
return (data[3] & 128) === 0 ? 4 : 5;
|
|
1472
|
+
source += (SCREEN_CAPTURE_MESSAGE_WIDTH - SCREEN_CAPTURE_WIDTH) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
2356
1473
|
}
|
|
1474
|
+
return pixels;
|
|
2357
1475
|
}
|
|
2358
1476
|
|
|
2359
1477
|
// src/VexConnection.ts
|
|
2360
1478
|
var thePacketEncoder = PacketEncoder.getInstance();
|
|
2361
|
-
var SCREEN_CAPTURE_HEIGHT = 272;
|
|
2362
|
-
var SCREEN_CAPTURE_WIDTH = 480;
|
|
2363
|
-
var SCREEN_CAPTURE_CHANNELS = 3;
|
|
2364
|
-
var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
|
|
2365
|
-
var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
|
|
2366
1479
|
|
|
2367
1480
|
class VexSerialConnection extends VexEventTarget {
|
|
2368
1481
|
filters = [{ usbVendorId: 10376 }];
|
|
@@ -2370,12 +1483,31 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2370
1483
|
reader;
|
|
2371
1484
|
port;
|
|
2372
1485
|
serial;
|
|
2373
|
-
|
|
1486
|
+
pendingCallbacks = new Map;
|
|
1487
|
+
rawCallbacks = {
|
|
1488
|
+
head: undefined,
|
|
1489
|
+
tail: undefined
|
|
1490
|
+
};
|
|
1491
|
+
pendingCommandTails = new Map;
|
|
2374
1492
|
_onPortDisconnect = null;
|
|
2375
1493
|
_closePromise = null;
|
|
1494
|
+
_openPromise = null;
|
|
1495
|
+
_isClosing = false;
|
|
2376
1496
|
_wasConnected = false;
|
|
2377
1497
|
fileTransferTail = Promise.resolve();
|
|
2378
1498
|
fileTransferDepth = 0;
|
|
1499
|
+
get callbacksQueue() {
|
|
1500
|
+
const callbacks = [];
|
|
1501
|
+
for (const queue of this.pendingCallbacks.values()) {
|
|
1502
|
+
for (let callback = queue.head;callback; callback = callback.next) {
|
|
1503
|
+
callbacks.push(callback);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
for (let callback = this.rawCallbacks.head;callback; callback = callback.next) {
|
|
1507
|
+
callbacks.push(callback);
|
|
1508
|
+
}
|
|
1509
|
+
return callbacks;
|
|
1510
|
+
}
|
|
2379
1511
|
get isConnected() {
|
|
2380
1512
|
return this.port !== undefined && this.reader !== undefined && this.writer !== undefined;
|
|
2381
1513
|
}
|
|
@@ -2386,25 +1518,46 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2386
1518
|
super();
|
|
2387
1519
|
this.serial = serial;
|
|
2388
1520
|
}
|
|
1521
|
+
reportWarning(message, details) {
|
|
1522
|
+
this.emitSafely("warning", {
|
|
1523
|
+
message,
|
|
1524
|
+
details
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
emitSafely(eventName, data) {
|
|
1528
|
+
try {
|
|
1529
|
+
this.emit(eventName, data);
|
|
1530
|
+
} catch {}
|
|
1531
|
+
}
|
|
2389
1532
|
async close() {
|
|
2390
1533
|
if (this._closePromise)
|
|
2391
1534
|
return this._closePromise;
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
this._closePromise =
|
|
1535
|
+
this._isClosing = true;
|
|
1536
|
+
const closing = this._closeAfterOpen();
|
|
1537
|
+
this._closePromise = closing;
|
|
2395
1538
|
try {
|
|
2396
|
-
await
|
|
1539
|
+
await closing;
|
|
2397
1540
|
} finally {
|
|
2398
|
-
this._closePromise
|
|
1541
|
+
if (this._closePromise === closing)
|
|
1542
|
+
this._closePromise = null;
|
|
1543
|
+
this._isClosing = false;
|
|
2399
1544
|
}
|
|
2400
1545
|
}
|
|
1546
|
+
async _closeAfterOpen() {
|
|
1547
|
+
const opening = this._openPromise;
|
|
1548
|
+
if (opening !== null)
|
|
1549
|
+
await opening;
|
|
1550
|
+
if (!this._hasOpenResources())
|
|
1551
|
+
return;
|
|
1552
|
+
await this._doClose();
|
|
1553
|
+
}
|
|
2401
1554
|
_hasOpenResources() {
|
|
2402
|
-
return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.
|
|
1555
|
+
return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.hasPendingCallbacks();
|
|
2403
1556
|
}
|
|
2404
1557
|
async _doClose() {
|
|
2405
|
-
for (const callback of this.
|
|
1558
|
+
for (const callback of this.drainPendingCallbacks()) {
|
|
2406
1559
|
clearTimeout(callback.timeout);
|
|
2407
|
-
callback.callback(
|
|
1560
|
+
callback.callback(258 /* NOT_CONNECTED */);
|
|
2408
1561
|
}
|
|
2409
1562
|
const onDisconnect = this._onPortDisconnect;
|
|
2410
1563
|
this._onPortDisconnect = null;
|
|
@@ -2448,38 +1601,48 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2448
1601
|
try {
|
|
2449
1602
|
await port.close();
|
|
2450
1603
|
} catch (e) {
|
|
2451
|
-
|
|
1604
|
+
this.reportWarning("failed to close the serial port", e);
|
|
2452
1605
|
}
|
|
2453
1606
|
}
|
|
2454
1607
|
if (this._wasConnected) {
|
|
2455
1608
|
this._wasConnected = false;
|
|
2456
|
-
this.
|
|
1609
|
+
this.emitSafely("disconnected", undefined);
|
|
2457
1610
|
}
|
|
2458
1611
|
}
|
|
2459
1612
|
open(use = 0, askUser = true) {
|
|
2460
|
-
if (this.
|
|
2461
|
-
return
|
|
2462
|
-
|
|
2463
|
-
|
|
1613
|
+
if (this._openPromise !== null)
|
|
1614
|
+
return new ResultAsync(this._openPromise);
|
|
1615
|
+
const opening = this._open(use, askUser);
|
|
1616
|
+
this._openPromise = opening;
|
|
1617
|
+
opening.then(() => {
|
|
1618
|
+
if (this._openPromise === opening)
|
|
1619
|
+
this._openPromise = null;
|
|
1620
|
+
}, () => {
|
|
1621
|
+
if (this._openPromise === opening)
|
|
1622
|
+
this._openPromise = null;
|
|
1623
|
+
});
|
|
1624
|
+
return new ResultAsync(opening);
|
|
2464
1625
|
}
|
|
2465
1626
|
async _open(use, askUser) {
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
return this.filters.find((f) => (f.usbVendorId === undefined || f.usbVendorId === info.usbVendorId) && (f.usbProductId === undefined || f.usbProductId === info.usbProductId));
|
|
2471
|
-
}).filter((candidate) => candidate.readable === null);
|
|
2472
|
-
port = ports[use];
|
|
1627
|
+
if (this._closePromise !== null)
|
|
1628
|
+
await this._closePromise;
|
|
1629
|
+
if (this.port !== undefined) {
|
|
1630
|
+
return err(new VexIoError("Already connected."));
|
|
2473
1631
|
}
|
|
1632
|
+
const ports = (await this.serial.getPorts()).filter((p) => {
|
|
1633
|
+
const info = p.getInfo();
|
|
1634
|
+
return this.filters.find((f) => (f.usbVendorId === undefined || f.usbVendorId === info.usbVendorId) && (f.usbProductId === undefined || f.usbProductId === info.usbProductId));
|
|
1635
|
+
}).filter((candidate) => candidate.readable === null);
|
|
1636
|
+
let port = ports[use];
|
|
2474
1637
|
if (port == null && askUser) {
|
|
2475
1638
|
try {
|
|
2476
1639
|
port = await this.serial.requestPort({ filters: this.filters });
|
|
2477
1640
|
} catch {}
|
|
2478
1641
|
}
|
|
2479
1642
|
if (port == null)
|
|
2480
|
-
return ok(
|
|
1643
|
+
return ok("no-port");
|
|
2481
1644
|
if (port.readable != null)
|
|
2482
|
-
return ok(
|
|
1645
|
+
return ok("busy");
|
|
2483
1646
|
try {
|
|
2484
1647
|
this.port = port;
|
|
2485
1648
|
await port.open({ baudRate: 115200 });
|
|
@@ -2491,46 +1654,79 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2491
1654
|
this.reader = this.port.readable.getReader();
|
|
2492
1655
|
this.startReader();
|
|
2493
1656
|
this._wasConnected = true;
|
|
2494
|
-
this.
|
|
2495
|
-
return ok(
|
|
2496
|
-
} catch {
|
|
2497
|
-
await this.
|
|
2498
|
-
return
|
|
1657
|
+
this.emitSafely("connected", undefined);
|
|
1658
|
+
return ok("opened");
|
|
1659
|
+
} catch (e) {
|
|
1660
|
+
await this._doClose();
|
|
1661
|
+
return err(toVexSerialError(e, "io"));
|
|
2499
1662
|
}
|
|
2500
1663
|
}
|
|
2501
|
-
writeData(rawData, resolve, timeout = 1000) {
|
|
2502
|
-
this.writeDataAsync(rawData, timeout).then(resolve);
|
|
2503
|
-
}
|
|
2504
1664
|
async writeDataAsync(rawData, timeout = 1000) {
|
|
1665
|
+
if (rawData instanceof DeviceBoundPacket) {
|
|
1666
|
+
return this.serializeCommand(rawData.commandId, rawData.commandExtendedId, () => this.writeDataAsyncUnserialized(rawData, timeout));
|
|
1667
|
+
}
|
|
1668
|
+
return this.writeDataAsyncUnserialized(rawData, timeout);
|
|
1669
|
+
}
|
|
1670
|
+
async serializeCommand(commandId, commandExtendedId, operation) {
|
|
1671
|
+
const key = `${commandId}:${commandExtendedId ?? ""}`;
|
|
1672
|
+
const previous = this.pendingCommandTails.get(key) ?? Promise.resolve();
|
|
1673
|
+
let release = () => {};
|
|
1674
|
+
const current = new Promise((resolve) => {
|
|
1675
|
+
release = resolve;
|
|
1676
|
+
});
|
|
1677
|
+
this.pendingCommandTails.set(key, current);
|
|
1678
|
+
await previous;
|
|
1679
|
+
try {
|
|
1680
|
+
return await operation();
|
|
1681
|
+
} finally {
|
|
1682
|
+
release();
|
|
1683
|
+
if (this.pendingCommandTails.get(key) === current) {
|
|
1684
|
+
this.pendingCommandTails.delete(key);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
async writeDataAsyncUnserialized(rawData, timeout) {
|
|
2505
1689
|
return new Promise((resolve) => {
|
|
2506
|
-
if (this.writer === undefined) {
|
|
2507
|
-
resolve(
|
|
1690
|
+
if (this.writer === undefined || this._isClosing) {
|
|
1691
|
+
resolve(258 /* NOT_CONNECTED */);
|
|
2508
1692
|
return;
|
|
2509
1693
|
}
|
|
2510
1694
|
const data = rawData instanceof DeviceBoundPacket ? rawData.data : rawData;
|
|
1695
|
+
const queue = rawData instanceof DeviceBoundPacket ? this.getPendingQueue(rawData.commandId, rawData.commandExtendedId, true) : this.rawCallbacks;
|
|
2511
1696
|
const cb = {
|
|
1697
|
+
active: true,
|
|
2512
1698
|
callback: resolve,
|
|
2513
1699
|
timeout: setTimeout(() => {
|
|
2514
|
-
|
|
2515
|
-
if (index === -1)
|
|
1700
|
+
if (!this.removePendingCallback(cb))
|
|
2516
1701
|
return;
|
|
2517
|
-
this.callbacksQueue.splice(index, 1);
|
|
2518
1702
|
cb.callback(256 /* TIMEOUT */);
|
|
2519
1703
|
}, timeout),
|
|
1704
|
+
next: undefined,
|
|
1705
|
+
previous: undefined,
|
|
1706
|
+
queue,
|
|
2520
1707
|
wantedCommandId: rawData instanceof DeviceBoundPacket ? rawData.commandId : undefined,
|
|
2521
1708
|
wantedCommandExId: rawData instanceof DeviceBoundPacket ? rawData.commandExtendedId : undefined
|
|
2522
1709
|
};
|
|
2523
|
-
this.
|
|
1710
|
+
this.enqueuePendingCallback(cb);
|
|
2524
1711
|
this.writer.write(data).catch(() => {
|
|
2525
|
-
|
|
2526
|
-
if (index === -1)
|
|
1712
|
+
if (!this.removePendingCallback(cb))
|
|
2527
1713
|
return;
|
|
2528
|
-
this.callbacksQueue.splice(index, 1);
|
|
2529
1714
|
clearTimeout(cb.timeout);
|
|
2530
1715
|
resolve(257 /* WRITE_ERROR */);
|
|
2531
1716
|
});
|
|
2532
1717
|
});
|
|
2533
1718
|
}
|
|
1719
|
+
request(packet, ReplyType, timeout = 1000) {
|
|
1720
|
+
return new ResultAsync((async () => {
|
|
1721
|
+
const result = await this.writeDataAsync(packet, timeout);
|
|
1722
|
+
if (result instanceof ReplyType)
|
|
1723
|
+
return ok(result);
|
|
1724
|
+
if (result === 258 /* NOT_CONNECTED */) {
|
|
1725
|
+
return err(new VexNotConnectedError);
|
|
1726
|
+
}
|
|
1727
|
+
return err(new VexProtocolError(expectedReplyMessage(packet, ReplyType, result), typeof result === "number" ? result : undefined));
|
|
1728
|
+
})());
|
|
1729
|
+
}
|
|
2534
1730
|
async readData(cache, expectedSize) {
|
|
2535
1731
|
if (this.reader == null)
|
|
2536
1732
|
throw new Error("No reader");
|
|
@@ -2538,93 +1734,172 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2538
1734
|
const { value: readData, done: isDone } = await this.reader.read();
|
|
2539
1735
|
if (isDone)
|
|
2540
1736
|
throw new Error("No data");
|
|
2541
|
-
cache
|
|
1737
|
+
cache.append(readData);
|
|
2542
1738
|
}
|
|
2543
|
-
return cache;
|
|
2544
1739
|
}
|
|
2545
1740
|
async startReader() {
|
|
2546
|
-
|
|
1741
|
+
const cache = new ReceiveBuffer;
|
|
2547
1742
|
let sliceIdx = 0;
|
|
2548
1743
|
for (;; )
|
|
2549
1744
|
try {
|
|
2550
|
-
|
|
1745
|
+
await this.readData(cache, 5);
|
|
2551
1746
|
sliceIdx = 0;
|
|
2552
|
-
while (!thePacketEncoder.validateHeader(cache)) {
|
|
2553
|
-
const
|
|
1747
|
+
while (!thePacketEncoder.validateHeader(cache.bytes)) {
|
|
1748
|
+
const bytes = cache.bytes;
|
|
1749
|
+
const nextHeader = bytes.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && bytes[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
|
|
2554
1750
|
if (nextHeader >= 0) {
|
|
2555
|
-
cache
|
|
1751
|
+
cache.discard(nextHeader);
|
|
2556
1752
|
} else {
|
|
2557
|
-
cache
|
|
1753
|
+
cache.discard(bytes.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : bytes.length);
|
|
2558
1754
|
}
|
|
2559
|
-
|
|
1755
|
+
await this.readData(cache, 5);
|
|
2560
1756
|
}
|
|
2561
|
-
const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache);
|
|
2562
|
-
const n = thePacketEncoder.getHostHeaderLength(cache);
|
|
1757
|
+
const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache.bytes);
|
|
1758
|
+
const n = thePacketEncoder.getHostHeaderLength(cache.bytes);
|
|
2563
1759
|
const totalSize = n + payloadExpectedSize;
|
|
2564
|
-
|
|
1760
|
+
await this.readData(cache, totalSize);
|
|
2565
1761
|
sliceIdx = totalSize;
|
|
2566
|
-
const
|
|
1762
|
+
const packet = cache.copy(totalSize);
|
|
1763
|
+
const cmdId = packet[2];
|
|
2567
1764
|
const hasExtId = cmdId === 88 || cmdId === 86;
|
|
2568
|
-
const cmdExId = hasExtId ?
|
|
2569
|
-
const ack =
|
|
1765
|
+
const cmdExId = hasExtId ? packet[n] : undefined;
|
|
1766
|
+
const ack = packet[n + 1];
|
|
2570
1767
|
if (hasExtId) {
|
|
2571
|
-
if (!thePacketEncoder.validateMessageCdc(
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
let tryIdx = 0;
|
|
2578
|
-
while ((callbackInfo = this.callbacksQueue[tryIdx++]) !== undefined) {
|
|
2579
|
-
wantedCmdId = callbackInfo?.wantedCommandId;
|
|
2580
|
-
wantedCmdExId = callbackInfo?.wantedCommandExId;
|
|
2581
|
-
if (wantedCmdId !== undefined && wantedCmdId !== cmdId || wantedCmdExId !== undefined && wantedCmdExId !== cmdExId) {
|
|
1768
|
+
if (!thePacketEncoder.validateMessageCdc(packet)) {
|
|
1769
|
+
this.reportWarning("discarding a reply with an invalid CDC CRC", {
|
|
1770
|
+
commandId: cmdId,
|
|
1771
|
+
commandExtendedId: cmdExId,
|
|
1772
|
+
ack
|
|
1773
|
+
});
|
|
2582
1774
|
continue;
|
|
2583
1775
|
}
|
|
2584
|
-
break;
|
|
2585
1776
|
}
|
|
1777
|
+
const callbackInfo = this.shiftPendingCallback(cmdId, cmdExId) ?? this.shiftRawCallback();
|
|
2586
1778
|
if (callbackInfo === undefined) {
|
|
2587
|
-
|
|
1779
|
+
this.reportWarning("received a reply with no matching request", {
|
|
1780
|
+
commandId: cmdId,
|
|
1781
|
+
commandExtendedId: cmdExId,
|
|
1782
|
+
ack
|
|
1783
|
+
});
|
|
2588
1784
|
continue;
|
|
2589
1785
|
}
|
|
2590
|
-
const
|
|
2591
|
-
const
|
|
2592
|
-
|
|
2593
|
-
|
|
1786
|
+
const wantedCmdId = callbackInfo.wantedCommandId;
|
|
1787
|
+
const wantedCmdExId = callbackInfo.wantedCommandExId;
|
|
1788
|
+
const PackageType = thePacketEncoder.getPacketType(wantedCmdId, wantedCmdExId);
|
|
1789
|
+
if (wantedCmdId === undefined || PackageType === undefined) {
|
|
1790
|
+
if (wantedCmdId !== undefined) {
|
|
1791
|
+
this.reportWarning("no packet class is registered for the wanted command", { commandId: wantedCmdId, commandExtendedId: wantedCmdExId });
|
|
1792
|
+
}
|
|
1793
|
+
callbackInfo.callback(packet.buffer);
|
|
2594
1794
|
} else {
|
|
2595
|
-
if (
|
|
2596
|
-
callbackInfo.callback(new PackageType(
|
|
1795
|
+
if (PackageType.isValidPacket(packet, n)) {
|
|
1796
|
+
callbackInfo.callback(new PackageType(packet));
|
|
2597
1797
|
} else {
|
|
2598
|
-
|
|
1798
|
+
this.reportWarning("reply failed packet validation; delivering its ack instead", { commandId: cmdId, commandExtendedId: cmdExId, ack });
|
|
2599
1799
|
callbackInfo.callback(ack);
|
|
2600
1800
|
}
|
|
2601
1801
|
}
|
|
2602
1802
|
clearTimeout(callbackInfo.timeout);
|
|
2603
|
-
this.callbacksQueue.splice(tryIdx - 1, 1);
|
|
2604
1803
|
} catch (e) {
|
|
2605
1804
|
if (!(e instanceof Error && e.message === "No data")) {
|
|
2606
|
-
|
|
1805
|
+
this.reportWarning("reader loop stopped by a read error", {
|
|
1806
|
+
error: e,
|
|
1807
|
+
pendingBytes: cache.bytes
|
|
1808
|
+
});
|
|
2607
1809
|
}
|
|
2608
1810
|
await this.close();
|
|
2609
1811
|
break;
|
|
2610
1812
|
} finally {
|
|
2611
|
-
cache
|
|
1813
|
+
cache.discard(sliceIdx);
|
|
2612
1814
|
}
|
|
2613
1815
|
}
|
|
2614
|
-
|
|
2615
|
-
return
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
1816
|
+
pendingKey(commandId, commandExtendedId) {
|
|
1817
|
+
return `${commandId}:${commandExtendedId ?? ""}`;
|
|
1818
|
+
}
|
|
1819
|
+
getPendingQueue(commandId, commandExtendedId, create) {
|
|
1820
|
+
const key = this.pendingKey(commandId, commandExtendedId);
|
|
1821
|
+
let queue = this.pendingCallbacks.get(key);
|
|
1822
|
+
if (queue === undefined && create) {
|
|
1823
|
+
const created = { head: undefined, tail: undefined };
|
|
1824
|
+
this.pendingCallbacks.set(key, created);
|
|
1825
|
+
queue = created;
|
|
1826
|
+
}
|
|
1827
|
+
return queue ?? { head: undefined, tail: undefined };
|
|
1828
|
+
}
|
|
1829
|
+
enqueuePendingCallback(callback) {
|
|
1830
|
+
const tail = callback.queue.tail;
|
|
1831
|
+
callback.previous = tail;
|
|
1832
|
+
if (tail === undefined)
|
|
1833
|
+
callback.queue.head = callback;
|
|
1834
|
+
else
|
|
1835
|
+
tail.next = callback;
|
|
1836
|
+
callback.queue.tail = callback;
|
|
1837
|
+
}
|
|
1838
|
+
removePendingCallback(callback) {
|
|
1839
|
+
if (!callback.active)
|
|
1840
|
+
return false;
|
|
1841
|
+
callback.active = false;
|
|
1842
|
+
const { queue, previous, next } = callback;
|
|
1843
|
+
if (previous === undefined)
|
|
1844
|
+
queue.head = next;
|
|
1845
|
+
else
|
|
1846
|
+
previous.next = next;
|
|
1847
|
+
if (next === undefined)
|
|
1848
|
+
queue.tail = previous;
|
|
1849
|
+
else
|
|
1850
|
+
next.previous = previous;
|
|
1851
|
+
callback.previous = undefined;
|
|
1852
|
+
callback.next = undefined;
|
|
1853
|
+
if (queue !== this.rawCallbacks && queue.head === undefined && callback.wantedCommandId !== undefined) {
|
|
1854
|
+
this.pendingCallbacks.delete(this.pendingKey(callback.wantedCommandId, callback.wantedCommandExId));
|
|
1855
|
+
}
|
|
1856
|
+
return true;
|
|
1857
|
+
}
|
|
1858
|
+
shiftPendingCallback(commandId, commandExtendedId) {
|
|
1859
|
+
const queue = this.getPendingQueue(commandId, commandExtendedId, false);
|
|
1860
|
+
const callback = queue.head;
|
|
1861
|
+
if (callback !== undefined)
|
|
1862
|
+
this.removePendingCallback(callback);
|
|
1863
|
+
return callback;
|
|
1864
|
+
}
|
|
1865
|
+
shiftRawCallback() {
|
|
1866
|
+
const callback = this.rawCallbacks.head;
|
|
1867
|
+
if (callback !== undefined)
|
|
1868
|
+
this.removePendingCallback(callback);
|
|
1869
|
+
return callback;
|
|
1870
|
+
}
|
|
1871
|
+
hasPendingCallbacks() {
|
|
1872
|
+
return this.pendingCallbacks.size > 0 || this.rawCallbacks.head !== undefined;
|
|
1873
|
+
}
|
|
1874
|
+
drainPendingCallbacks() {
|
|
1875
|
+
const callbacks = [];
|
|
1876
|
+
for (const queue of this.pendingCallbacks.values()) {
|
|
1877
|
+
for (let callback = queue.head;callback; ) {
|
|
1878
|
+
const next = callback.next;
|
|
1879
|
+
callback.active = false;
|
|
1880
|
+
callback.previous = undefined;
|
|
1881
|
+
callback.next = undefined;
|
|
1882
|
+
callbacks.push(callback);
|
|
1883
|
+
callback = next;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
for (let callback = this.rawCallbacks.head;callback; ) {
|
|
1887
|
+
const next = callback.next;
|
|
1888
|
+
callback.active = false;
|
|
1889
|
+
callback.previous = undefined;
|
|
1890
|
+
callback.next = undefined;
|
|
1891
|
+
callbacks.push(callback);
|
|
1892
|
+
callback = next;
|
|
1893
|
+
}
|
|
1894
|
+
this.pendingCallbacks.clear();
|
|
1895
|
+
this.rawCallbacks = { head: undefined, tail: undefined };
|
|
1896
|
+
return callbacks;
|
|
1897
|
+
}
|
|
1898
|
+
query1() {
|
|
1899
|
+
return this.request(new Query1H2DPacket, Query1ReplyD2HPacket, 100);
|
|
2619
1900
|
}
|
|
2620
1901
|
getSystemVersion() {
|
|
2621
|
-
return new
|
|
2622
|
-
const result = await this.writeDataAsync(new SystemVersionH2DPacket);
|
|
2623
|
-
if (result instanceof SystemVersionReplyD2HPacket) {
|
|
2624
|
-
return ok(result.version);
|
|
2625
|
-
}
|
|
2626
|
-
return err(new VexProtocolError("system version was not acknowledged"));
|
|
2627
|
-
})());
|
|
1902
|
+
return this.request(new SystemVersionH2DPacket, SystemVersionReplyD2HPacket).map((result) => result.version);
|
|
2628
1903
|
}
|
|
2629
1904
|
}
|
|
2630
1905
|
|
|
@@ -2651,34 +1926,19 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2651
1926
|
}
|
|
2652
1927
|
}
|
|
2653
1928
|
getDeviceStatus() {
|
|
2654
|
-
return new
|
|
2655
|
-
const result = await this.writeDataAsync(new GetDeviceStatusH2DPacket);
|
|
2656
|
-
return result instanceof GetDeviceStatusReplyD2HPacket ? ok(result) : err(new VexProtocolError("device status was not acknowledged"));
|
|
2657
|
-
})());
|
|
1929
|
+
return this.request(new GetDeviceStatusH2DPacket, GetDeviceStatusReplyD2HPacket);
|
|
2658
1930
|
}
|
|
2659
1931
|
getRadioStatus() {
|
|
2660
|
-
return new
|
|
2661
|
-
const result = await this.writeDataAsync(new GetRadioStatusH2DPacket);
|
|
2662
|
-
return result instanceof GetRadioStatusReplyD2HPacket ? ok(result) : err(new VexProtocolError("radio status was not acknowledged"));
|
|
2663
|
-
})());
|
|
1932
|
+
return this.request(new GetRadioStatusH2DPacket, GetRadioStatusReplyD2HPacket);
|
|
2664
1933
|
}
|
|
2665
1934
|
getSystemFlags() {
|
|
2666
|
-
return new
|
|
2667
|
-
const result = await this.writeDataAsync(new GetSystemFlagsH2DPacket);
|
|
2668
|
-
return result instanceof GetSystemFlagsReplyD2HPacket ? ok(result) : err(new VexProtocolError("system flags were not acknowledged"));
|
|
2669
|
-
})());
|
|
1935
|
+
return this.request(new GetSystemFlagsH2DPacket, GetSystemFlagsReplyD2HPacket);
|
|
2670
1936
|
}
|
|
2671
1937
|
getSystemStatus(timeout = 1000) {
|
|
2672
|
-
return new
|
|
2673
|
-
const result = await this.writeDataAsync(new GetSystemStatusH2DPacket, timeout);
|
|
2674
|
-
return result instanceof GetSystemStatusReplyD2HPacket ? ok(result) : err(new VexProtocolError("system status was not acknowledged"));
|
|
2675
|
-
})());
|
|
1938
|
+
return this.request(new GetSystemStatusH2DPacket, GetSystemStatusReplyD2HPacket, timeout);
|
|
2676
1939
|
}
|
|
2677
1940
|
getMatchStatus() {
|
|
2678
|
-
return new
|
|
2679
|
-
const result = await this.writeDataAsync(new GetMatchStatusH2DPacket);
|
|
2680
|
-
return result instanceof MatchStatusReplyD2HPacket ? ok(result) : err(new VexProtocolError("match status was not acknowledged"));
|
|
2681
|
-
})());
|
|
1941
|
+
return this.request(new GetMatchStatusH2DPacket, MatchStatusReplyD2HPacket);
|
|
2682
1942
|
}
|
|
2683
1943
|
uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback) {
|
|
2684
1944
|
return wrapTransfer(this, () => this._uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback));
|
|
@@ -2739,32 +1999,35 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2739
1999
|
async _downloadFileToHostUnlocked(request, downloadTarget, progressCallback) {
|
|
2740
2000
|
const { filename, vendor, loadAddress, size } = request;
|
|
2741
2001
|
let nextAddress = loadAddress ?? USER_FLASH_USR_CODE_START;
|
|
2742
|
-
const
|
|
2743
|
-
if (
|
|
2744
|
-
return err(
|
|
2745
|
-
}
|
|
2002
|
+
const p1Result = await this.request(new InitFileTransferH2DPacket(2 /* READ */, downloadTarget, vendor, 0 /* NONE */, new Uint8Array, nextAddress, filename, ""), InitFileTransferReplyD2HPacket);
|
|
2003
|
+
if (p1Result.isErr())
|
|
2004
|
+
return err(p1Result.error);
|
|
2746
2005
|
let transferFailed = true;
|
|
2747
2006
|
let result = ok(new Uint8Array);
|
|
2748
2007
|
try {
|
|
2008
|
+
const p1 = p1Result.value;
|
|
2749
2009
|
const fileSize = size ?? p1.fileSize;
|
|
2750
2010
|
const bufferChunkSize = getTransferChunkSize(p1.windowSize);
|
|
2751
2011
|
let bufferOffset = 0;
|
|
2752
2012
|
const fileBuf = new Uint8Array(fileSize);
|
|
2753
2013
|
while (bufferOffset < fileSize) {
|
|
2754
|
-
const
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2014
|
+
const remainingSize = fileSize - bufferOffset;
|
|
2015
|
+
const chunkSize = Math.min(bufferChunkSize, remainingSize);
|
|
2016
|
+
const requestedSize = chunkSize + 3 & ~3;
|
|
2017
|
+
const p2Result = await this.request(new ReadFileH2DPacket(nextAddress, requestedSize), ReadFileReplyD2HPacket, 3000);
|
|
2018
|
+
if (p2Result.isErr())
|
|
2019
|
+
throw p2Result.error;
|
|
2020
|
+
const p2 = p2Result.value;
|
|
2759
2021
|
if (p2.addr !== nextAddress) {
|
|
2760
2022
|
throw new VexTransferError(`ReadFileReplyD2HPacket returned address ${p2.addr}, expected ${nextAddress}`);
|
|
2761
2023
|
}
|
|
2762
2024
|
if (p2.length <= 0 || p2.length > requestedSize || p2.buf.byteLength !== p2.length) {
|
|
2763
2025
|
throw new VexTransferError(`ReadFileReplyD2HPacket returned invalid length ${p2.length}`);
|
|
2764
2026
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2027
|
+
const receivedSize = Math.min(p2.length, remainingSize);
|
|
2028
|
+
fileBuf.set(p2.buf.subarray(0, receivedSize), bufferOffset);
|
|
2029
|
+
bufferOffset += receivedSize;
|
|
2030
|
+
nextAddress += receivedSize;
|
|
2768
2031
|
progressCallback?.(bufferOffset, fileSize);
|
|
2769
2032
|
}
|
|
2770
2033
|
transferFailed = false;
|
|
@@ -2773,11 +2036,12 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2773
2036
|
result = err(e instanceof VexSerialError ? e : toVexSerialError(e, "transfer"));
|
|
2774
2037
|
} finally {
|
|
2775
2038
|
try {
|
|
2776
|
-
await this.
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2039
|
+
const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
|
|
2040
|
+
if (!transferFailed && exitResult.isErr())
|
|
2041
|
+
result = err(exitResult.error);
|
|
2042
|
+
} catch (e) {
|
|
2043
|
+
if (!transferFailed)
|
|
2044
|
+
result = err(toVexSerialError(e, "io"));
|
|
2781
2045
|
}
|
|
2782
2046
|
}
|
|
2783
2047
|
return result;
|
|
@@ -2802,22 +2066,20 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2802
2066
|
downloadTarget = downloadTarget ?? 1 /* FILE_TARGET_QSPI */;
|
|
2803
2067
|
vendor = vendor ?? 1 /* USER */;
|
|
2804
2068
|
let nextAddress = loadAddress ?? USER_FLASH_USR_CODE_START;
|
|
2805
|
-
const
|
|
2806
|
-
if (
|
|
2807
|
-
return err(
|
|
2808
|
-
|
|
2069
|
+
const p1Result = await this.request(new InitFileTransferH2DPacket(1 /* WRITE */, downloadTarget, vendor, 1 /* OVERWRITE */, buf, nextAddress, filename, exttype), InitFileTransferReplyD2HPacket);
|
|
2070
|
+
if (p1Result.isErr())
|
|
2071
|
+
return err(p1Result.error);
|
|
2072
|
+
const p1 = p1Result.value;
|
|
2809
2073
|
const bufferChunkSize = getTransferChunkSize(p1.windowSize);
|
|
2810
2074
|
let bufferOffset = 0;
|
|
2811
2075
|
let lastBlock = false;
|
|
2812
2076
|
let transferFailed = true;
|
|
2813
|
-
let exitReply;
|
|
2814
2077
|
let result = ok(false);
|
|
2815
2078
|
try {
|
|
2816
2079
|
if (linkedFile !== undefined) {
|
|
2817
|
-
const
|
|
2818
|
-
if (
|
|
2819
|
-
throw
|
|
2820
|
-
}
|
|
2080
|
+
const p3Result = await this.request(new LinkFileH2DPacket(linkedFile.vendor ?? 1 /* USER */, linkedFile.filename, 0), LinkFileReplyD2HPacket, 1e4);
|
|
2081
|
+
if (p3Result.isErr())
|
|
2082
|
+
throw p3Result.error;
|
|
2821
2083
|
}
|
|
2822
2084
|
while (!lastBlock) {
|
|
2823
2085
|
let tmpbuf;
|
|
@@ -2829,23 +2091,21 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2829
2091
|
tmpbuf.set(buf.subarray(bufferOffset, buf.byteLength));
|
|
2830
2092
|
lastBlock = true;
|
|
2831
2093
|
}
|
|
2832
|
-
const
|
|
2833
|
-
if (
|
|
2834
|
-
throw
|
|
2835
|
-
if (progressCallback != null)
|
|
2836
|
-
progressCallback(bufferOffset, buf.byteLength);
|
|
2094
|
+
const p2Result = await this.request(new WriteFileH2DPacket(nextAddress, tmpbuf), WriteFileReplyD2HPacket, 3000);
|
|
2095
|
+
if (p2Result.isErr())
|
|
2096
|
+
throw p2Result.error;
|
|
2837
2097
|
bufferOffset += bufferChunkSize;
|
|
2838
2098
|
nextAddress += bufferChunkSize;
|
|
2099
|
+
progressCallback?.(Math.min(bufferOffset, buf.byteLength), buf.byteLength);
|
|
2839
2100
|
}
|
|
2840
|
-
progressCallback?.(buf.byteLength, buf.byteLength);
|
|
2841
2101
|
transferFailed = false;
|
|
2842
2102
|
} catch (e) {
|
|
2843
2103
|
result = err(e instanceof VexSerialError ? e : toVexSerialError(e, "transfer"));
|
|
2844
2104
|
} finally {
|
|
2845
2105
|
try {
|
|
2846
|
-
|
|
2106
|
+
const exitResult = await this.request(new ExitFileTransferH2DPacket(transferFailed ? 3 /* EXIT_HALT */ : autoRun ? 1 /* EXIT_RUN */ : 3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
|
|
2847
2107
|
if (!transferFailed) {
|
|
2848
|
-
result =
|
|
2108
|
+
result = exitResult.map(() => true);
|
|
2849
2109
|
}
|
|
2850
2110
|
} catch (cleanupError) {
|
|
2851
2111
|
if (!transferFailed) {
|
|
@@ -2867,14 +2127,20 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2867
2127
|
}
|
|
2868
2128
|
let result;
|
|
2869
2129
|
try {
|
|
2870
|
-
const
|
|
2871
|
-
result =
|
|
2130
|
+
const eraseResult = await this.request(new EraseFileH2DPacket(vendor, filename), EraseFileReplyD2HPacket);
|
|
2131
|
+
result = eraseResult.map(() => {
|
|
2132
|
+
return;
|
|
2133
|
+
});
|
|
2872
2134
|
} catch (e) {
|
|
2873
2135
|
result = err(toVexSerialError(e, "io"));
|
|
2874
|
-
}
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2136
|
+
}
|
|
2137
|
+
try {
|
|
2138
|
+
const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
|
|
2139
|
+
if (result.isOk() && exitResult.isErr())
|
|
2140
|
+
result = err(exitResult.error);
|
|
2141
|
+
} catch (e) {
|
|
2142
|
+
if (result.isOk())
|
|
2143
|
+
result = err(toVexSerialError(e, "io"));
|
|
2878
2144
|
}
|
|
2879
2145
|
return result;
|
|
2880
2146
|
}));
|
|
@@ -2883,23 +2149,26 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2883
2149
|
return new ResultAsync(this.withFileTransfer(async () => {
|
|
2884
2150
|
let result;
|
|
2885
2151
|
try {
|
|
2886
|
-
const
|
|
2887
|
-
result =
|
|
2152
|
+
const clearResult = await this.request(new FileClearUpH2DPacket(1 /* USER */), FileClearUpReplyD2HPacket, 30000);
|
|
2153
|
+
result = clearResult.map(() => {
|
|
2154
|
+
return;
|
|
2155
|
+
});
|
|
2888
2156
|
} catch (e) {
|
|
2889
2157
|
result = err(toVexSerialError(e, "io"));
|
|
2890
|
-
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2158
|
+
}
|
|
2159
|
+
try {
|
|
2160
|
+
const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
|
|
2161
|
+
if (result.isOk() && exitResult.isErr())
|
|
2162
|
+
result = err(exitResult.error);
|
|
2163
|
+
} catch (e) {
|
|
2164
|
+
if (result.isOk())
|
|
2165
|
+
result = err(toVexSerialError(e, "io"));
|
|
2894
2166
|
}
|
|
2895
2167
|
return result;
|
|
2896
2168
|
}));
|
|
2897
2169
|
}
|
|
2898
2170
|
captureScreenSetup() {
|
|
2899
|
-
return new
|
|
2900
|
-
const result = await this.writeDataAsync(new ScreenCaptureH2DPacket(0));
|
|
2901
|
-
return result instanceof ScreenCaptureReplyD2HPacket ? ok(result) : err(new VexProtocolError("screen capture was rejected"));
|
|
2902
|
-
})());
|
|
2171
|
+
return this.request(new ScreenCaptureH2DPacket(0), ScreenCaptureReplyD2HPacket);
|
|
2903
2172
|
}
|
|
2904
2173
|
captureScreen(progressCallback) {
|
|
2905
2174
|
return wrapTransfer(this, () => this._captureScreen(progressCallback));
|
|
@@ -2913,74 +2182,85 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2913
2182
|
filename: "screen",
|
|
2914
2183
|
vendor: 15 /* SYS */,
|
|
2915
2184
|
loadAddress: 0,
|
|
2916
|
-
size:
|
|
2185
|
+
size: SCREEN_CAPTURE_FRAMEBUFFER_SIZE
|
|
2917
2186
|
}, 2 /* FILE_TARGET_CBUF */, progressCallback);
|
|
2918
2187
|
if (framebuffer.isErr())
|
|
2919
2188
|
return err(framebuffer.error);
|
|
2920
2189
|
return ok(convertScreenCapture(framebuffer.value));
|
|
2921
2190
|
}
|
|
2922
2191
|
setMatchMode(mode) {
|
|
2923
|
-
return new
|
|
2924
|
-
const result = await this.writeDataAsync(new UpdateMatchModeH2DPacket(mode, 0));
|
|
2925
|
-
return result instanceof MatchModeReplyD2HPacket ? ok(result) : err(new VexProtocolError("setMatchMode was not acknowledged"));
|
|
2926
|
-
})());
|
|
2192
|
+
return this.request(new UpdateMatchModeH2DPacket(mode, 0), MatchModeReplyD2HPacket);
|
|
2927
2193
|
}
|
|
2928
2194
|
runProgram(value) {
|
|
2929
2195
|
return this.loadProgram(value);
|
|
2930
2196
|
}
|
|
2931
2197
|
loadProgram(value) {
|
|
2932
|
-
return new
|
|
2933
|
-
const result = await this.writeDataAsync(new LoadFileActionH2DPacket(1 /* USER */, 0 /* RUN */, value));
|
|
2934
|
-
return result instanceof LoadFileActionReplyD2HPacket ? ok(result) : err(new VexProtocolError("loadProgram was not acknowledged"));
|
|
2935
|
-
})());
|
|
2198
|
+
return this.request(new LoadFileActionH2DPacket(1 /* USER */, 0 /* RUN */, value), LoadFileActionReplyD2HPacket);
|
|
2936
2199
|
}
|
|
2937
2200
|
stopProgram() {
|
|
2938
|
-
return new
|
|
2939
|
-
const result = await this.writeDataAsync(new LoadFileActionH2DPacket(1 /* USER */, 128 /* STOP */, ""));
|
|
2940
|
-
return result instanceof LoadFileActionReplyD2HPacket ? ok(result) : err(new VexProtocolError("stopProgram was not acknowledged"));
|
|
2941
|
-
})());
|
|
2201
|
+
return this.request(new LoadFileActionH2DPacket(1 /* USER */, 128 /* STOP */, ""), LoadFileActionReplyD2HPacket);
|
|
2942
2202
|
}
|
|
2943
2203
|
mockTouch(x, y, press) {
|
|
2944
|
-
return new
|
|
2945
|
-
const result = await this.writeDataAsync(new SendDashTouchH2DPacket(x, y, press));
|
|
2946
|
-
return result instanceof SendDashTouchReplyD2HPacket ? ok(result) : err(new VexProtocolError("mockTouch was not acknowledged"));
|
|
2947
|
-
})());
|
|
2204
|
+
return this.request(new SendDashTouchH2DPacket(x, y, press), SendDashTouchReplyD2HPacket);
|
|
2948
2205
|
}
|
|
2949
2206
|
openScreen(screen, port) {
|
|
2950
|
-
return new
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2207
|
+
return this.request(new SelectDashH2DPacket(screen, port), SelectDashReplyD2HPacket);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
class ReceiveBuffer {
|
|
2212
|
+
storage = new Uint8Array(0);
|
|
2213
|
+
start = 0;
|
|
2214
|
+
end = 0;
|
|
2215
|
+
get byteLength() {
|
|
2216
|
+
return this.end - this.start;
|
|
2217
|
+
}
|
|
2218
|
+
get bytes() {
|
|
2219
|
+
return this.storage.subarray(this.start, this.end);
|
|
2220
|
+
}
|
|
2221
|
+
append(chunk) {
|
|
2222
|
+
this.reserve(chunk.byteLength);
|
|
2223
|
+
this.storage.set(chunk, this.end);
|
|
2224
|
+
this.end += chunk.byteLength;
|
|
2225
|
+
}
|
|
2226
|
+
copy(length) {
|
|
2227
|
+
return this.bytes.slice(0, length);
|
|
2228
|
+
}
|
|
2229
|
+
discard(length) {
|
|
2230
|
+
this.start += length < 0 ? this.byteLength + length : length;
|
|
2231
|
+
if (this.start < 0)
|
|
2232
|
+
this.start = 0;
|
|
2233
|
+
if (this.start > this.end)
|
|
2234
|
+
this.start = this.end;
|
|
2235
|
+
if (this.start === this.end) {
|
|
2236
|
+
this.start = 0;
|
|
2237
|
+
this.end = 0;
|
|
2238
|
+
} else if (this.start >= 4096 && this.start * 2 >= this.storage.length) {
|
|
2239
|
+
this.storage.copyWithin(0, this.start, this.end);
|
|
2240
|
+
this.end -= this.start;
|
|
2241
|
+
this.start = 0;
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
reserve(additional) {
|
|
2245
|
+
const required = this.byteLength + additional;
|
|
2246
|
+
if (this.storage.length - this.end >= additional)
|
|
2247
|
+
return;
|
|
2248
|
+
if (this.storage.length >= required) {
|
|
2249
|
+
this.storage.copyWithin(0, this.start, this.end);
|
|
2250
|
+
this.end = this.byteLength;
|
|
2251
|
+
this.start = 0;
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
const storage = new Uint8Array(Math.max(required, Math.max(64, this.storage.length * 2)));
|
|
2255
|
+
storage.set(this.bytes);
|
|
2256
|
+
this.end = this.byteLength;
|
|
2257
|
+
this.start = 0;
|
|
2258
|
+
this.storage = storage;
|
|
2954
2259
|
}
|
|
2955
2260
|
}
|
|
2956
|
-
function binaryArrayJoin(left, right) {
|
|
2957
|
-
const leftSize = left != null ? left.byteLength : 0;
|
|
2958
|
-
const rightSize = right != null ? right.byteLength : 0;
|
|
2959
|
-
const all = new Uint8Array(leftSize + rightSize);
|
|
2960
|
-
if (all.length === 0)
|
|
2961
|
-
return new Uint8Array;
|
|
2962
|
-
if (left != null)
|
|
2963
|
-
all.set(new Uint8Array(left), 0);
|
|
2964
|
-
if (right != null)
|
|
2965
|
-
all.set(new Uint8Array(right), leftSize);
|
|
2966
|
-
return all;
|
|
2967
|
-
}
|
|
2968
2261
|
function getTransferChunkSize(windowSize) {
|
|
2969
2262
|
return windowSize > 0 && windowSize <= USER_PROG_CHUNK_SIZE ? windowSize : USER_PROG_CHUNK_SIZE;
|
|
2970
2263
|
}
|
|
2971
|
-
function convertScreenCapture(framebuffer) {
|
|
2972
|
-
const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
|
|
2973
|
-
for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
|
|
2974
|
-
for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
|
|
2975
|
-
const source = (row * SCREEN_CAPTURE_MESSAGE_WIDTH + column) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
2976
|
-
const target = (row * SCREEN_CAPTURE_WIDTH + column) * SCREEN_CAPTURE_CHANNELS;
|
|
2977
|
-
pixels[target] = framebuffer[source + 2] ?? 0;
|
|
2978
|
-
pixels[target + 1] = framebuffer[source + 1] ?? 0;
|
|
2979
|
-
pixels[target + 2] = framebuffer[source] ?? 0;
|
|
2980
|
-
}
|
|
2981
|
-
}
|
|
2982
|
-
return pixels;
|
|
2983
|
-
}
|
|
2984
2264
|
function wrapTransfer(conn, operation) {
|
|
2985
2265
|
return new ResultAsync(conn.withFileTransfer(async () => {
|
|
2986
2266
|
try {
|
|
@@ -2992,17 +2272,26 @@ function wrapTransfer(conn, operation) {
|
|
|
2992
2272
|
}
|
|
2993
2273
|
}));
|
|
2994
2274
|
}
|
|
2995
|
-
function
|
|
2996
|
-
|
|
2275
|
+
function expectedReplyMessage(packet, ReplyType, reply) {
|
|
2276
|
+
const expected = `expected ${ReplyType.name} for ${packet.constructor.name}`;
|
|
2277
|
+
if (typeof reply === "number")
|
|
2278
|
+
return `${expected}; received ${ackTypeName(reply)}`;
|
|
2279
|
+
if (reply instanceof ArrayBuffer)
|
|
2280
|
+
return `${expected}; received raw ArrayBuffer`;
|
|
2281
|
+
return `${expected}; received ${reply.constructor.name}`;
|
|
2282
|
+
}
|
|
2283
|
+
function ackTypeName(ackType) {
|
|
2284
|
+
return `AckType.${AckType[ackType] ?? "UNKNOWN"} (${ackType})`;
|
|
2997
2285
|
}
|
|
2286
|
+
// src/VexDeviceState.ts
|
|
2287
|
+
import { err as err4, ok as ok4, ResultAsync as ResultAsync4 } from "neverthrow";
|
|
2288
|
+
|
|
2998
2289
|
// src/VexFirmware.ts
|
|
2290
|
+
import { err as err2, errAsync, ok as ok2, ResultAsync as ResultAsync2 } from "neverthrow";
|
|
2999
2291
|
var MAX_CATALOG_BYTES = 4 * 1024;
|
|
3000
2292
|
var MAX_VEXOS_BYTES = 64 * 1024 * 1024;
|
|
3001
2293
|
var MAX_FIRMWARE_IMAGE_BYTES = 32 * 1024 * 1024;
|
|
3002
2294
|
var MAX_AGGREGATE_IMAGE_BYTES = 48 * 1024 * 1024;
|
|
3003
|
-
function fromAsyncFn(fn) {
|
|
3004
|
-
return new ResultAsync(fn());
|
|
3005
|
-
}
|
|
3006
2295
|
function downloadFileFromInternet(link, options = {}) {
|
|
3007
2296
|
const { maxBytes = Number.POSITIVE_INFINITY, timeout = 30000 } = options;
|
|
3008
2297
|
if (maxBytes <= 0) {
|
|
@@ -3011,7 +2300,7 @@ function downloadFileFromInternet(link, options = {}) {
|
|
|
3011
2300
|
if (timeout < 0) {
|
|
3012
2301
|
return errAsync(new VexInvalidArgumentError("timeout must be non-negative"));
|
|
3013
2302
|
}
|
|
3014
|
-
return
|
|
2303
|
+
return new ResultAsync2(runDownload(link, maxBytes, timeout));
|
|
3015
2304
|
}
|
|
3016
2305
|
async function runDownload(link, maxBytes, timeout) {
|
|
3017
2306
|
const controller = new AbortController;
|
|
@@ -3021,20 +2310,20 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
3021
2310
|
try {
|
|
3022
2311
|
response = await fetch(link, { signal: controller.signal });
|
|
3023
2312
|
} catch (e) {
|
|
3024
|
-
return
|
|
2313
|
+
return err2(new VexDownloadError(`failed to download ${link} (${e instanceof Error ? e.message : String(e)})`));
|
|
3025
2314
|
}
|
|
3026
2315
|
if (!response.ok) {
|
|
3027
|
-
return
|
|
2316
|
+
return err2(new VexDownloadError(`failed to download ${link} (${response.status})`));
|
|
3028
2317
|
}
|
|
3029
2318
|
const declaredLength = response.headers.get("content-length");
|
|
3030
2319
|
if (declaredLength !== null) {
|
|
3031
2320
|
const declared = Number.parseInt(declaredLength, 10);
|
|
3032
2321
|
if (!Number.isNaN(declared) && declared > 0 && declared > maxBytes) {
|
|
3033
|
-
return
|
|
2322
|
+
return err2(new VexDownloadError(`declared content length ${declared} exceeds limit ${maxBytes} for ${link}`));
|
|
3034
2323
|
}
|
|
3035
2324
|
}
|
|
3036
2325
|
if (response.body == null) {
|
|
3037
|
-
return
|
|
2326
|
+
return err2(new VexDownloadError(`no response body for ${link}`));
|
|
3038
2327
|
}
|
|
3039
2328
|
const reader = response.body.getReader();
|
|
3040
2329
|
const chunks = [];
|
|
@@ -3051,7 +2340,7 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
3051
2340
|
try {
|
|
3052
2341
|
await reader.cancel();
|
|
3053
2342
|
} catch {}
|
|
3054
|
-
return
|
|
2343
|
+
return err2(new VexDownloadError(`downloaded body exceeds limit ${maxBytes} for ${link}`));
|
|
3055
2344
|
}
|
|
3056
2345
|
chunks.push(value);
|
|
3057
2346
|
}
|
|
@@ -3066,7 +2355,7 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
3066
2355
|
result.set(chunk, offset);
|
|
3067
2356
|
offset += chunk.byteLength;
|
|
3068
2357
|
}
|
|
3069
|
-
return
|
|
2358
|
+
return ok2(result.buffer);
|
|
3070
2359
|
} finally {
|
|
3071
2360
|
clearTimeout(timer);
|
|
3072
2361
|
}
|
|
@@ -3078,23 +2367,23 @@ function sleepUntilAsync(f, timeout, interval = 20) {
|
|
|
3078
2367
|
if (interval <= 0) {
|
|
3079
2368
|
return errAsync(new VexInvalidArgumentError("interval must be positive"));
|
|
3080
2369
|
}
|
|
3081
|
-
return
|
|
2370
|
+
return new ResultAsync2(runSleepUntilAsync(f, timeout, interval));
|
|
3082
2371
|
}
|
|
3083
2372
|
async function runSleepUntilAsync(f, timeout, interval) {
|
|
3084
2373
|
const deadline = Date.now() + timeout;
|
|
3085
2374
|
while (Date.now() <= deadline) {
|
|
3086
2375
|
try {
|
|
3087
2376
|
if (await f())
|
|
3088
|
-
return
|
|
2377
|
+
return ok2(true);
|
|
3089
2378
|
} catch (e) {
|
|
3090
|
-
return
|
|
2379
|
+
return err2(toVexSerialError(e, "io"));
|
|
3091
2380
|
}
|
|
3092
2381
|
const remaining = deadline - Date.now();
|
|
3093
2382
|
if (remaining <= 0)
|
|
3094
2383
|
break;
|
|
3095
2384
|
await sleepInner(Math.min(interval, remaining));
|
|
3096
2385
|
}
|
|
3097
|
-
return
|
|
2386
|
+
return ok2(false);
|
|
3098
2387
|
}
|
|
3099
2388
|
function sleepUntil(f, timeout, interval = 20) {
|
|
3100
2389
|
if (timeout < 0) {
|
|
@@ -3103,33 +2392,86 @@ function sleepUntil(f, timeout, interval = 20) {
|
|
|
3103
2392
|
if (interval <= 0) {
|
|
3104
2393
|
return errAsync(new VexInvalidArgumentError("interval must be positive"));
|
|
3105
2394
|
}
|
|
3106
|
-
return
|
|
2395
|
+
return new ResultAsync2(runSleepUntil(f, timeout, interval));
|
|
3107
2396
|
}
|
|
3108
2397
|
async function runSleepUntil(f, timeout, interval) {
|
|
3109
2398
|
const deadline = Date.now() + timeout;
|
|
3110
2399
|
while (Date.now() <= deadline) {
|
|
3111
2400
|
try {
|
|
3112
2401
|
if (f())
|
|
3113
|
-
return
|
|
2402
|
+
return ok2(true);
|
|
3114
2403
|
} catch (e) {
|
|
3115
|
-
return
|
|
2404
|
+
return err2(toVexSerialError(e, "io"));
|
|
3116
2405
|
}
|
|
3117
2406
|
const remaining = deadline - Date.now();
|
|
3118
2407
|
if (remaining <= 0)
|
|
3119
2408
|
break;
|
|
3120
2409
|
await sleepInner(Math.min(interval, remaining));
|
|
3121
2410
|
}
|
|
3122
|
-
return
|
|
2411
|
+
return ok2(false);
|
|
3123
2412
|
}
|
|
3124
2413
|
function sleep(ms) {
|
|
3125
2414
|
if (ms < 0) {
|
|
3126
2415
|
return errAsync(new VexInvalidArgumentError("ms must be non-negative"));
|
|
3127
2416
|
}
|
|
3128
|
-
return
|
|
2417
|
+
return ResultAsync2.fromSafePromise(sleepInner(ms));
|
|
3129
2418
|
}
|
|
3130
2419
|
async function sleepInner(ms) {
|
|
3131
2420
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3132
2421
|
}
|
|
2422
|
+
async function flashFactoryImage(conn, options, pcb) {
|
|
2423
|
+
const { image, label, downloadTarget } = options;
|
|
2424
|
+
pcb(`FACTORY ENB ${label}`, 0, 0);
|
|
2425
|
+
const enableReply = await conn.request(new FactoryEnableH2DPacket, FactoryEnableReplyD2HPacket);
|
|
2426
|
+
if (enableReply.isErr())
|
|
2427
|
+
return err2(enableReply.error);
|
|
2428
|
+
const writeRequest = {
|
|
2429
|
+
filename: "null.bin",
|
|
2430
|
+
vendor: 1 /* USER */,
|
|
2431
|
+
loadAddress: USER_FLASH_USR_CODE_START,
|
|
2432
|
+
buf: image.buf,
|
|
2433
|
+
downloadTarget,
|
|
2434
|
+
exttype: "bin",
|
|
2435
|
+
autoRun: true,
|
|
2436
|
+
linkedFile: undefined
|
|
2437
|
+
};
|
|
2438
|
+
const upload = await conn.uploadFileToDeviceUnlocked(writeRequest, (c, t) => {
|
|
2439
|
+
pcb(`UPLOAD ${label}`, c, t);
|
|
2440
|
+
});
|
|
2441
|
+
if (upload.isErr())
|
|
2442
|
+
return err2(upload.error);
|
|
2443
|
+
if (!upload.value) {
|
|
2444
|
+
return err2(new VexFirmwareError(`${label} upload was rejected by device`));
|
|
2445
|
+
}
|
|
2446
|
+
const deadline = Date.now() + 120000;
|
|
2447
|
+
while (Date.now() < deadline) {
|
|
2448
|
+
const statusReply = await conn.request(new FactoryStatusH2DPacket, FactoryStatusReplyD2HPacket, 1e4);
|
|
2449
|
+
if (statusReply.isErr())
|
|
2450
|
+
return err2(statusReply.error);
|
|
2451
|
+
reportFactoryStatus(label, statusReply.value, pcb);
|
|
2452
|
+
if (statusReply.value.status === 0 && statusReply.value.percent === 100) {
|
|
2453
|
+
return ok2(undefined);
|
|
2454
|
+
}
|
|
2455
|
+
await sleepInner(500);
|
|
2456
|
+
}
|
|
2457
|
+
return err2(new VexFirmwareError(`${label} factory status timed out`));
|
|
2458
|
+
}
|
|
2459
|
+
function reportFactoryStatus(label, reply, pcb) {
|
|
2460
|
+
switch (reply.status) {
|
|
2461
|
+
case 2:
|
|
2462
|
+
pcb(`ERASE ${label}`, reply.percent, 100);
|
|
2463
|
+
break;
|
|
2464
|
+
case 3:
|
|
2465
|
+
pcb(`WRITE ${label}`, reply.percent, 100);
|
|
2466
|
+
break;
|
|
2467
|
+
case 4:
|
|
2468
|
+
pcb(`VERIFY ${label}`, reply.percent, 100);
|
|
2469
|
+
break;
|
|
2470
|
+
case 8:
|
|
2471
|
+
pcb(`FINISHING ${label}`, reply.percent, 100);
|
|
2472
|
+
break;
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
3133
2475
|
async function extractFirmwareImages(usingVersion, vexos) {
|
|
3134
2476
|
const { unzip } = await import("unzipit");
|
|
3135
2477
|
const { entries } = await unzip(vexos);
|
|
@@ -3177,13 +2519,13 @@ async function extractFirmwareImages(usingVersion, vexos) {
|
|
|
3177
2519
|
return ordered;
|
|
3178
2520
|
}
|
|
3179
2521
|
function uploadFirmware(state, publicUrl = "https://content.vexrobotics.com/vexos/public/V5/", usingVersion, progressCallback) {
|
|
3180
|
-
return
|
|
2522
|
+
return new ResultAsync2(runUploadFirmware(state, publicUrl, usingVersion, progressCallback));
|
|
3181
2523
|
}
|
|
3182
2524
|
async function runUploadFirmware(state, publicUrl, usingVersion, progressCallback) {
|
|
3183
2525
|
const device = state._instance;
|
|
3184
2526
|
const conn = device.connection;
|
|
3185
2527
|
if (conn == null || !conn.isConnected) {
|
|
3186
|
-
return
|
|
2528
|
+
return err2(new VexNotConnectedError);
|
|
3187
2529
|
}
|
|
3188
2530
|
const pcb = progressCallback ?? (() => {});
|
|
3189
2531
|
let version = usingVersion;
|
|
@@ -3193,20 +2535,20 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
|
|
|
3193
2535
|
maxBytes: MAX_CATALOG_BYTES
|
|
3194
2536
|
});
|
|
3195
2537
|
if (catalog.isErr())
|
|
3196
|
-
return
|
|
2538
|
+
return err2(catalog.error);
|
|
3197
2539
|
version = new TextDecoder().decode(catalog.value).trim();
|
|
3198
2540
|
pcb("FETCH CATALOG", 1, 1);
|
|
3199
2541
|
}
|
|
3200
2542
|
if (!/^[A-Za-z0-9._-]+$/.test(version)) {
|
|
3201
|
-
return
|
|
2543
|
+
return err2(new VexFirmwareError(`invalid VEXos version: ${version}`));
|
|
3202
2544
|
}
|
|
3203
2545
|
pcb("FETCH VEXOS", 0, 1);
|
|
3204
2546
|
const vexosResult = await downloadFileFromInternet(publicUrl + version + ".vexos", { maxBytes: MAX_VEXOS_BYTES });
|
|
3205
2547
|
if (vexosResult.isErr())
|
|
3206
|
-
return
|
|
2548
|
+
return err2(vexosResult.error);
|
|
3207
2549
|
const vexos = vexosResult.value;
|
|
3208
2550
|
if (vexos.byteLength === 0) {
|
|
3209
|
-
return
|
|
2551
|
+
return err2(new VexFirmwareError("VEXos archive is empty"));
|
|
3210
2552
|
}
|
|
3211
2553
|
pcb("FETCH VEXOS", 1, 1);
|
|
3212
2554
|
pcb("UNZIP VEXOS", 0, 1);
|
|
@@ -3215,189 +2557,104 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
|
|
|
3215
2557
|
images = await extractFirmwareImages(version, vexos);
|
|
3216
2558
|
} catch (e) {
|
|
3217
2559
|
if (e instanceof VexSerialError)
|
|
3218
|
-
return
|
|
3219
|
-
return
|
|
2560
|
+
return err2(e);
|
|
2561
|
+
return err2(toVexSerialError(e, "firmware"));
|
|
3220
2562
|
}
|
|
3221
2563
|
pcb("UNZIP VEXOS", 1, 1);
|
|
3222
|
-
return state.
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
|
|
3229
|
-
if (boot === undefined) {
|
|
3230
|
-
return err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
|
|
3231
|
-
}
|
|
3232
|
-
const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
|
|
3233
|
-
if (assertImage === undefined) {
|
|
3234
|
-
return err(new VexFirmwareError("VEXos archive is missing assets.bin"));
|
|
3235
|
-
}
|
|
3236
|
-
const bootWriteRequest = {
|
|
3237
|
-
filename: "null.bin",
|
|
3238
|
-
vendor: 1 /* USER */,
|
|
3239
|
-
loadAddress: USER_FLASH_USR_CODE_START,
|
|
3240
|
-
buf: boot.buf,
|
|
3241
|
-
downloadTarget: 14 /* FILE_TARGET_B1 */,
|
|
3242
|
-
exttype: "bin",
|
|
3243
|
-
autoRun: true,
|
|
3244
|
-
linkedFile: undefined
|
|
3245
|
-
};
|
|
3246
|
-
const bootUpload = await conn.uploadFileToDevice(bootWriteRequest, (c, t) => {
|
|
3247
|
-
pcb("UPLOAD BOOT", c, t);
|
|
3248
|
-
});
|
|
3249
|
-
if (bootUpload.isErr())
|
|
3250
|
-
return err(bootUpload.error);
|
|
3251
|
-
if (!bootUpload.value)
|
|
3252
|
-
return ok(false);
|
|
3253
|
-
const bootDeadline = Date.now() + 120000;
|
|
3254
|
-
let bootComplete = false;
|
|
3255
|
-
while (Date.now() < bootDeadline) {
|
|
3256
|
-
const result3 = await conn.writeDataAsync(new FactoryStatusH2DPacket, 1e4);
|
|
3257
|
-
if (result3 instanceof FactoryStatusReplyD2HPacket) {
|
|
3258
|
-
switch (result3.status) {
|
|
3259
|
-
case 2:
|
|
3260
|
-
pcb("ERASE BOOT", result3.percent, 100);
|
|
3261
|
-
break;
|
|
3262
|
-
case 3:
|
|
3263
|
-
pcb("WRITE BOOT", result3.percent, 100);
|
|
3264
|
-
break;
|
|
3265
|
-
case 4:
|
|
3266
|
-
pcb("VERIFY BOOT", result3.percent, 100);
|
|
3267
|
-
break;
|
|
3268
|
-
case 8:
|
|
3269
|
-
pcb("FINISHING BOOT", result3.percent, 100);
|
|
3270
|
-
break;
|
|
3271
|
-
}
|
|
3272
|
-
if (result3.status === 0 && result3.percent === 100) {
|
|
3273
|
-
bootComplete = true;
|
|
3274
|
-
break;
|
|
3275
|
-
}
|
|
3276
|
-
} else {
|
|
3277
|
-
return ok(false);
|
|
3278
|
-
}
|
|
3279
|
-
await sleepInner(500);
|
|
3280
|
-
}
|
|
3281
|
-
if (!bootComplete)
|
|
3282
|
-
return ok(false);
|
|
3283
|
-
pcb("FACTORY ENB ASSERT", 0, 0);
|
|
3284
|
-
const result5 = await conn.writeDataAsync(new FactoryEnableH2DPacket);
|
|
3285
|
-
if (!(result5 instanceof FactoryEnableReplyD2HPacket)) {
|
|
3286
|
-
return err(new VexProtocolError("FactoryEnableH2DPacket failed"));
|
|
3287
|
-
}
|
|
3288
|
-
const assertWriteRequest = {
|
|
3289
|
-
filename: "null.bin",
|
|
3290
|
-
vendor: 1 /* USER */,
|
|
3291
|
-
loadAddress: USER_FLASH_USR_CODE_START,
|
|
3292
|
-
buf: assertImage.buf,
|
|
3293
|
-
downloadTarget: 13 /* FILE_TARGET_A1 */,
|
|
3294
|
-
exttype: "bin",
|
|
3295
|
-
autoRun: true,
|
|
3296
|
-
linkedFile: undefined
|
|
3297
|
-
};
|
|
3298
|
-
const assertUpload = await conn.uploadFileToDevice(assertWriteRequest, (c, t) => {
|
|
3299
|
-
pcb("UPLOAD ASSERT", c, t);
|
|
3300
|
-
});
|
|
3301
|
-
if (assertUpload.isErr())
|
|
3302
|
-
return err(assertUpload.error);
|
|
3303
|
-
if (!assertUpload.value)
|
|
3304
|
-
return ok(false);
|
|
3305
|
-
const assertDeadline = Date.now() + 120000;
|
|
3306
|
-
let assertComplete = false;
|
|
3307
|
-
while (Date.now() < assertDeadline) {
|
|
3308
|
-
const result7 = await conn.writeDataAsync(new FactoryStatusH2DPacket, 1e4);
|
|
3309
|
-
if (result7 instanceof FactoryStatusReplyD2HPacket) {
|
|
3310
|
-
switch (result7.status) {
|
|
3311
|
-
case 2:
|
|
3312
|
-
pcb("ERASE ASSERT", result7.percent, 100);
|
|
3313
|
-
break;
|
|
3314
|
-
case 3:
|
|
3315
|
-
pcb("WRITE ASSERT", result7.percent, 100);
|
|
3316
|
-
break;
|
|
3317
|
-
case 4:
|
|
3318
|
-
pcb("VERIFY ASSERT", result7.percent, 100);
|
|
3319
|
-
break;
|
|
3320
|
-
case 8:
|
|
3321
|
-
pcb("FINISHING ASSERT", result7.percent, 100);
|
|
3322
|
-
break;
|
|
2564
|
+
return state.withRefreshPaused(async () => {
|
|
2565
|
+
try {
|
|
2566
|
+
return await conn.withFileTransfer(async () => {
|
|
2567
|
+
const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
|
|
2568
|
+
if (boot === undefined) {
|
|
2569
|
+
return err2(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
|
|
3323
2570
|
}
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
2571
|
+
const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
|
|
2572
|
+
if (assertImage === undefined) {
|
|
2573
|
+
return err2(new VexFirmwareError("VEXos archive is missing assets.bin"));
|
|
3327
2574
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
2575
|
+
const bootFlash = await flashFactoryImage(conn, {
|
|
2576
|
+
image: boot,
|
|
2577
|
+
label: "BOOT",
|
|
2578
|
+
downloadTarget: 14 /* FILE_TARGET_B1 */
|
|
2579
|
+
}, pcb);
|
|
2580
|
+
if (bootFlash.isErr())
|
|
2581
|
+
return err2(bootFlash.error);
|
|
2582
|
+
const assetsFlash = await flashFactoryImage(conn, {
|
|
2583
|
+
image: assertImage,
|
|
2584
|
+
label: "ASSETS",
|
|
2585
|
+
downloadTarget: 13 /* FILE_TARGET_A1 */
|
|
2586
|
+
}, pcb);
|
|
2587
|
+
if (assetsFlash.isErr())
|
|
2588
|
+
return err2(assetsFlash.error);
|
|
2589
|
+
return ok2(true);
|
|
2590
|
+
});
|
|
2591
|
+
} catch (e) {
|
|
2592
|
+
return err2(toVexSerialError(e, "firmware"));
|
|
3332
2593
|
}
|
|
3333
|
-
if (!assertComplete)
|
|
3334
|
-
return ok(false);
|
|
3335
|
-
return ok(true);
|
|
3336
2594
|
});
|
|
3337
2595
|
}
|
|
3338
2596
|
|
|
3339
2597
|
// src/VexTransfers.ts
|
|
2598
|
+
import { err as err3, ok as ok3, ResultAsync as ResultAsync3 } from "neverthrow";
|
|
3340
2599
|
function getValue(state, key) {
|
|
3341
|
-
return new
|
|
2600
|
+
return new ResultAsync3((async () => {
|
|
3342
2601
|
const conn = state._instance.connection;
|
|
3343
2602
|
if (conn == null || !conn.isConnected) {
|
|
3344
|
-
return
|
|
2603
|
+
return err3(new VexNotConnectedError);
|
|
3345
2604
|
}
|
|
3346
|
-
|
|
3347
|
-
return result instanceof ReadKeyValueReplyD2HPacket ? ok(result.value) : err(new VexProtocolError("getValue was not acknowledged"));
|
|
2605
|
+
return conn.request(new ReadKeyValueH2DPacket(key), ReadKeyValueReplyD2HPacket).map((result) => result.value);
|
|
3348
2606
|
})());
|
|
3349
2607
|
}
|
|
3350
2608
|
function setValue(state, key, value) {
|
|
3351
|
-
return new
|
|
2609
|
+
return new ResultAsync3((async () => {
|
|
3352
2610
|
const conn = state._instance.connection;
|
|
3353
2611
|
if (conn == null || !conn.isConnected) {
|
|
3354
|
-
return
|
|
2612
|
+
return err3(new VexNotConnectedError);
|
|
3355
2613
|
}
|
|
3356
|
-
|
|
3357
|
-
|
|
2614
|
+
return conn.request(new WriteKeyValueH2DPacket(key, value), WriteKeyValueReplyD2HPacket).map(() => {
|
|
2615
|
+
return;
|
|
2616
|
+
});
|
|
3358
2617
|
})());
|
|
3359
2618
|
}
|
|
3360
2619
|
function listFiles(state, vendor = 1 /* USER */) {
|
|
3361
|
-
return new
|
|
2620
|
+
return new ResultAsync3((async () => {
|
|
3362
2621
|
const conn = state._instance.connection;
|
|
3363
2622
|
if (conn == null || !conn.isConnected) {
|
|
3364
|
-
return
|
|
3365
|
-
}
|
|
3366
|
-
const result = await conn.writeDataAsync(new GetDirectoryFileCountH2DPacket(vendor));
|
|
3367
|
-
if (!(result instanceof GetDirectoryFileCountReplyD2HPacket)) {
|
|
3368
|
-
return err(new VexProtocolError("directory file count was not acknowledged"));
|
|
2623
|
+
return err3(new VexNotConnectedError);
|
|
3369
2624
|
}
|
|
2625
|
+
const countResult = await conn.request(new GetDirectoryFileCountH2DPacket(vendor), GetDirectoryFileCountReplyD2HPacket);
|
|
2626
|
+
if (countResult.isErr())
|
|
2627
|
+
return err3(countResult.error);
|
|
3370
2628
|
const files = [];
|
|
3371
|
-
for (let i = 0;i <
|
|
3372
|
-
const
|
|
3373
|
-
if (
|
|
3374
|
-
return
|
|
3375
|
-
|
|
3376
|
-
if (result2.file != null) {
|
|
2629
|
+
for (let i = 0;i < countResult.value.count; i++) {
|
|
2630
|
+
const entryResult = await conn.request(new GetDirectoryEntryH2DPacket(i), GetDirectoryEntryReplyD2HPacket);
|
|
2631
|
+
if (entryResult.isErr())
|
|
2632
|
+
return err3(entryResult.error);
|
|
2633
|
+
if (entryResult.value.file != null) {
|
|
3377
2634
|
files.push({
|
|
3378
|
-
filename:
|
|
2635
|
+
filename: entryResult.value.file.filename,
|
|
3379
2636
|
vendor,
|
|
3380
|
-
loadAddress:
|
|
3381
|
-
size:
|
|
3382
|
-
crc32:
|
|
3383
|
-
type:
|
|
3384
|
-
timestamp:
|
|
3385
|
-
version:
|
|
2637
|
+
loadAddress: entryResult.value.file.loadAddress,
|
|
2638
|
+
size: entryResult.value.file.size,
|
|
2639
|
+
crc32: entryResult.value.file.crc32,
|
|
2640
|
+
type: entryResult.value.file.type,
|
|
2641
|
+
timestamp: entryResult.value.file.timestamp,
|
|
2642
|
+
version: entryResult.value.file.version
|
|
3386
2643
|
});
|
|
3387
2644
|
}
|
|
3388
2645
|
}
|
|
3389
|
-
return
|
|
2646
|
+
return ok3(files);
|
|
3390
2647
|
})());
|
|
3391
2648
|
}
|
|
3392
2649
|
function listProgram(state) {
|
|
3393
|
-
return new
|
|
2650
|
+
return new ResultAsync3((async () => {
|
|
3394
2651
|
const conn = state._instance.connection;
|
|
3395
2652
|
if (conn == null || !conn.isConnected) {
|
|
3396
|
-
return
|
|
2653
|
+
return err3(new VexNotConnectedError);
|
|
3397
2654
|
}
|
|
3398
2655
|
const files = await listFiles(state, 1 /* USER */);
|
|
3399
2656
|
if (files.isErr())
|
|
3400
|
-
return
|
|
2657
|
+
return err3(files.error);
|
|
3401
2658
|
const programList = [];
|
|
3402
2659
|
const iniFiles = files.value.filter((file) => file.filename.endsWith(".ini"));
|
|
3403
2660
|
for (let i = 0;i < iniFiles.length; i++) {
|
|
@@ -3418,21 +2675,21 @@ function listProgram(state) {
|
|
|
3418
2675
|
time: n,
|
|
3419
2676
|
requestedSlot: -1
|
|
3420
2677
|
};
|
|
3421
|
-
const
|
|
3422
|
-
if (
|
|
3423
|
-
program.slot =
|
|
3424
|
-
program.requestedSlot =
|
|
2678
|
+
const slotInfo = await conn.request(new GetProgramSlotInfoH2DPacket(1 /* USER */, program.binfile), GetProgramSlotInfoReplyD2HPacket);
|
|
2679
|
+
if (slotInfo.isOk()) {
|
|
2680
|
+
program.slot = slotInfo.value.slot;
|
|
2681
|
+
program.requestedSlot = slotInfo.value.requestedSlot;
|
|
3425
2682
|
}
|
|
3426
2683
|
programList.push(program);
|
|
3427
2684
|
}
|
|
3428
|
-
return
|
|
2685
|
+
return ok3(programList);
|
|
3429
2686
|
})());
|
|
3430
2687
|
}
|
|
3431
2688
|
function readFile(state, request, downloadTarget = 1 /* FILE_TARGET_QSPI */, progressCallback) {
|
|
3432
|
-
return new
|
|
2689
|
+
return new ResultAsync3((async () => {
|
|
3433
2690
|
const conn = state._instance.connection;
|
|
3434
2691
|
if (conn == null || !conn.isConnected) {
|
|
3435
|
-
return
|
|
2692
|
+
return err3(new VexNotConnectedError);
|
|
3436
2693
|
}
|
|
3437
2694
|
let handle;
|
|
3438
2695
|
if (typeof request === "string") {
|
|
@@ -3440,83 +2697,83 @@ function readFile(state, request, downloadTarget = 1 /* FILE_TARGET_QSPI */, pro
|
|
|
3440
2697
|
} else {
|
|
3441
2698
|
handle = request;
|
|
3442
2699
|
}
|
|
3443
|
-
return state.
|
|
2700
|
+
return state.withRefreshPaused(() => conn.downloadFileToHost(handle, downloadTarget, progressCallback));
|
|
3444
2701
|
})());
|
|
3445
2702
|
}
|
|
3446
2703
|
function removeFile(state, request) {
|
|
3447
|
-
return new
|
|
2704
|
+
return new ResultAsync3((async () => {
|
|
3448
2705
|
const conn = state._instance.connection;
|
|
3449
2706
|
if (conn == null || !conn.isConnected) {
|
|
3450
|
-
return
|
|
2707
|
+
return err3(new VexNotConnectedError);
|
|
3451
2708
|
}
|
|
3452
|
-
return state.
|
|
2709
|
+
return state.withRefreshPaused(() => conn.removeFile(request));
|
|
3453
2710
|
})());
|
|
3454
2711
|
}
|
|
3455
2712
|
function removeAllFiles(state) {
|
|
3456
|
-
return new
|
|
2713
|
+
return new ResultAsync3((async () => {
|
|
3457
2714
|
const conn = state._instance.connection;
|
|
3458
2715
|
if (conn == null || !conn.isConnected) {
|
|
3459
|
-
return
|
|
2716
|
+
return err3(new VexNotConnectedError);
|
|
3460
2717
|
}
|
|
3461
|
-
return state.
|
|
2718
|
+
return state.withRefreshPaused(() => conn.removeAllFiles());
|
|
3462
2719
|
})());
|
|
3463
2720
|
}
|
|
3464
2721
|
function uploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback) {
|
|
3465
|
-
return new
|
|
2722
|
+
return new ResultAsync3(runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback));
|
|
3466
2723
|
}
|
|
3467
2724
|
async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback) {
|
|
3468
2725
|
const device = state._instance;
|
|
3469
2726
|
const conn = device.connection;
|
|
3470
2727
|
if (conn == null || !conn.isConnected) {
|
|
3471
|
-
return
|
|
2728
|
+
return err3(new VexNotConnectedError);
|
|
3472
2729
|
}
|
|
3473
2730
|
let switchedToDownload = false;
|
|
3474
|
-
return state.
|
|
2731
|
+
return state.withRefreshPaused(async () => {
|
|
3475
2732
|
try {
|
|
3476
2733
|
if (device.isV5Controller) {
|
|
3477
2734
|
await sleep(250);
|
|
3478
2735
|
const refreshed = await device.refresh();
|
|
3479
2736
|
if (refreshed.isErr() || !refreshed.value) {
|
|
3480
|
-
return
|
|
2737
|
+
return err3(new VexProtocolError("device is unavailable"));
|
|
3481
2738
|
}
|
|
3482
2739
|
progressCallback("CHANNEL", 0, 1);
|
|
3483
2740
|
const p1 = await device.radio.changeChannel(1 /* DOWNLOAD */);
|
|
3484
2741
|
if (p1.isErr())
|
|
3485
|
-
return
|
|
2742
|
+
return err3(p1.error);
|
|
3486
2743
|
switchedToDownload = true;
|
|
3487
2744
|
await sleep(250);
|
|
3488
2745
|
const transferred = await sleepUntilAsync(async () => (await conn?.getSystemStatus(150))?.isOk() ?? false, 1e4, 200);
|
|
3489
2746
|
if (transferred.isErr())
|
|
3490
|
-
return
|
|
2747
|
+
return err3(transferred.error);
|
|
3491
2748
|
if (!transferred.value) {
|
|
3492
|
-
return
|
|
2749
|
+
return err3(new VexProtocolError("channel switch timed out"));
|
|
3493
2750
|
}
|
|
3494
2751
|
progressCallback("CHANNEL", 1, 1);
|
|
3495
2752
|
}
|
|
3496
2753
|
const p2 = await conn.uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback);
|
|
3497
2754
|
if (p2.isErr())
|
|
3498
|
-
return
|
|
2755
|
+
return err3(p2.error);
|
|
3499
2756
|
if (!p2.value)
|
|
3500
|
-
return
|
|
2757
|
+
return err3(new VexProtocolError("program upload rejected"));
|
|
3501
2758
|
if (device.isV5Controller) {
|
|
3502
2759
|
if (!device.brain.isAvailable) {
|
|
3503
|
-
return
|
|
2760
|
+
return err3(new VexProtocolError("brain unavailable after upload"));
|
|
3504
2761
|
}
|
|
3505
2762
|
progressCallback("CHANNEL", 0, 1);
|
|
3506
2763
|
const p3 = await device.radio.changeChannel(0 /* PIT */);
|
|
3507
2764
|
if (p3.isErr())
|
|
3508
|
-
return
|
|
2765
|
+
return err3(p3.error);
|
|
3509
2766
|
switchedToDownload = false;
|
|
3510
2767
|
await sleep(250);
|
|
3511
2768
|
const transferred = await sleepUntilAsync(async () => (await conn?.getSystemStatus(150))?.isOk() ?? false, 1e4, 200);
|
|
3512
2769
|
if (transferred.isErr())
|
|
3513
|
-
return
|
|
2770
|
+
return err3(transferred.error);
|
|
3514
2771
|
if (!transferred.value) {
|
|
3515
|
-
return
|
|
2772
|
+
return err3(new VexProtocolError("channel switch timed out"));
|
|
3516
2773
|
}
|
|
3517
2774
|
progressCallback("CHANNEL", 1, 1);
|
|
3518
2775
|
}
|
|
3519
|
-
return
|
|
2776
|
+
return ok3(true);
|
|
3520
2777
|
} finally {
|
|
3521
2778
|
if (switchedToDownload) {
|
|
3522
2779
|
await device.radio.changeChannel(0 /* PIT */);
|
|
@@ -3525,21 +2782,21 @@ async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progr
|
|
|
3525
2782
|
});
|
|
3526
2783
|
}
|
|
3527
2784
|
function writeFile(state, request, progressCallback) {
|
|
3528
|
-
return new
|
|
2785
|
+
return new ResultAsync3((async () => {
|
|
3529
2786
|
const conn = state._instance.connection;
|
|
3530
2787
|
if (conn == null || !conn.isConnected) {
|
|
3531
|
-
return
|
|
2788
|
+
return err3(new VexNotConnectedError);
|
|
3532
2789
|
}
|
|
3533
|
-
return state.
|
|
2790
|
+
return state.withRefreshPaused(() => conn.uploadFileToDevice(request, progressCallback));
|
|
3534
2791
|
})());
|
|
3535
2792
|
}
|
|
3536
2793
|
function captureScreen(state, progressCallback) {
|
|
3537
|
-
return new
|
|
2794
|
+
return new ResultAsync3((async () => {
|
|
3538
2795
|
const conn = state._instance.connection;
|
|
3539
2796
|
if (conn == null || !conn.isConnected) {
|
|
3540
|
-
return
|
|
2797
|
+
return err3(new VexNotConnectedError);
|
|
3541
2798
|
}
|
|
3542
|
-
return state.
|
|
2799
|
+
return state.withRefreshPaused(() => conn.captureScreen(progressCallback));
|
|
3543
2800
|
})());
|
|
3544
2801
|
}
|
|
3545
2802
|
|
|
@@ -3562,10 +2819,13 @@ class VexSerialDevice extends VexEventTarget {
|
|
|
3562
2819
|
class V5SerialDeviceState {
|
|
3563
2820
|
_instance;
|
|
3564
2821
|
refreshPauseDepth = 0;
|
|
3565
|
-
get
|
|
2822
|
+
get isRefreshPaused() {
|
|
3566
2823
|
return this.refreshPauseDepth > 0;
|
|
3567
2824
|
}
|
|
3568
|
-
|
|
2825
|
+
get isFileTransferring() {
|
|
2826
|
+
return this.isRefreshPaused;
|
|
2827
|
+
}
|
|
2828
|
+
async withRefreshPaused(operation) {
|
|
3569
2829
|
this.refreshPauseDepth++;
|
|
3570
2830
|
try {
|
|
3571
2831
|
return await operation();
|
|
@@ -3573,6 +2833,9 @@ class V5SerialDeviceState {
|
|
|
3573
2833
|
this.refreshPauseDepth--;
|
|
3574
2834
|
}
|
|
3575
2835
|
}
|
|
2836
|
+
async withFileTransfer(operation) {
|
|
2837
|
+
return this.withRefreshPaused(operation);
|
|
2838
|
+
}
|
|
3576
2839
|
brain = {
|
|
3577
2840
|
activeProgram: 0,
|
|
3578
2841
|
battery: {
|
|
@@ -3602,7 +2865,8 @@ class V5SerialDeviceState {
|
|
|
3602
2865
|
},
|
|
3603
2866
|
{
|
|
3604
2867
|
battery: 0,
|
|
3605
|
-
isAvailable: false
|
|
2868
|
+
isAvailable: false,
|
|
2869
|
+
isCharging: false
|
|
3606
2870
|
}
|
|
3607
2871
|
];
|
|
3608
2872
|
devices = [];
|
|
@@ -3625,8 +2889,14 @@ class V5SerialDeviceState {
|
|
|
3625
2889
|
|
|
3626
2890
|
class V5Brain {
|
|
3627
2891
|
state;
|
|
2892
|
+
batteryFacade;
|
|
2893
|
+
buttonFacade;
|
|
2894
|
+
settingsFacade;
|
|
3628
2895
|
constructor(state) {
|
|
3629
2896
|
this.state = state;
|
|
2897
|
+
this.batteryFacade = new V5Battery(state);
|
|
2898
|
+
this.buttonFacade = new V5BrainButton(state);
|
|
2899
|
+
this.settingsFacade = new V5BrainSettings(state);
|
|
3630
2900
|
}
|
|
3631
2901
|
get isRunningProgram() {
|
|
3632
2902
|
return this.activeProgram !== 0;
|
|
@@ -3638,50 +2908,49 @@ class V5Brain {
|
|
|
3638
2908
|
this.setActiveProgram(value).mapErr(() => {});
|
|
3639
2909
|
}
|
|
3640
2910
|
setActiveProgram(value) {
|
|
3641
|
-
return new
|
|
2911
|
+
return new ResultAsync4((async () => {
|
|
3642
2912
|
if (this.state.brain.activeProgram === value)
|
|
3643
|
-
return
|
|
2913
|
+
return ok4(undefined);
|
|
3644
2914
|
const conn = this.state._instance.connection;
|
|
3645
2915
|
if (conn == null)
|
|
3646
|
-
return
|
|
2916
|
+
return err4(new VexNotConnectedError);
|
|
3647
2917
|
const result = value === 0 ? await conn.stopProgram() : await conn.loadProgram(value);
|
|
3648
2918
|
if (result.isErr())
|
|
3649
|
-
return
|
|
2919
|
+
return err4(result.error);
|
|
3650
2920
|
this.state.brain.activeProgram = value;
|
|
3651
|
-
return
|
|
2921
|
+
return ok4(undefined);
|
|
3652
2922
|
})());
|
|
3653
2923
|
}
|
|
3654
2924
|
runProgram(slot) {
|
|
3655
|
-
return new
|
|
2925
|
+
return new ResultAsync4((async () => {
|
|
3656
2926
|
const conn = this.state._instance.connection;
|
|
3657
2927
|
if (conn == null)
|
|
3658
|
-
return
|
|
2928
|
+
return err4(new VexNotConnectedError);
|
|
3659
2929
|
const reply = await conn.runProgram(slot);
|
|
3660
2930
|
if (reply.isErr())
|
|
3661
|
-
return
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
return ok(undefined);
|
|
2931
|
+
return err4(reply.error);
|
|
2932
|
+
if (typeof slot === "number")
|
|
2933
|
+
this.state.brain.activeProgram = slot;
|
|
2934
|
+
return ok4(undefined);
|
|
3666
2935
|
})());
|
|
3667
2936
|
}
|
|
3668
2937
|
stopProgram() {
|
|
3669
|
-
return new
|
|
2938
|
+
return new ResultAsync4((async () => {
|
|
3670
2939
|
const conn = this.state._instance.connection;
|
|
3671
2940
|
if (conn == null)
|
|
3672
|
-
return
|
|
2941
|
+
return err4(new VexNotConnectedError);
|
|
3673
2942
|
const reply = await conn.stopProgram();
|
|
3674
2943
|
if (reply.isErr())
|
|
3675
|
-
return
|
|
2944
|
+
return err4(reply.error);
|
|
3676
2945
|
this.state.brain.activeProgram = 0;
|
|
3677
|
-
return
|
|
2946
|
+
return ok4(undefined);
|
|
3678
2947
|
})());
|
|
3679
2948
|
}
|
|
3680
2949
|
get battery() {
|
|
3681
|
-
return
|
|
2950
|
+
return this.batteryFacade;
|
|
3682
2951
|
}
|
|
3683
2952
|
get button() {
|
|
3684
|
-
return
|
|
2953
|
+
return this.buttonFacade;
|
|
3685
2954
|
}
|
|
3686
2955
|
get cpu0Version() {
|
|
3687
2956
|
return this.state.brain.cpu0Version;
|
|
@@ -3693,7 +2962,7 @@ class V5Brain {
|
|
|
3693
2962
|
return this.state.brain.isAvailable;
|
|
3694
2963
|
}
|
|
3695
2964
|
get settings() {
|
|
3696
|
-
return
|
|
2965
|
+
return this.settingsFacade;
|
|
3697
2966
|
}
|
|
3698
2967
|
get systemVersion() {
|
|
3699
2968
|
return this.state.brain.systemVersion;
|
|
@@ -3847,66 +3116,145 @@ class V5Radio {
|
|
|
3847
3116
|
return this.state.radio.latency;
|
|
3848
3117
|
}
|
|
3849
3118
|
changeChannel(channel) {
|
|
3850
|
-
return new
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3119
|
+
return new ResultAsync4((async () => {
|
|
3120
|
+
const conn = this.state._instance.connection;
|
|
3121
|
+
if (conn == null || !conn.isConnected) {
|
|
3122
|
+
return err4(new VexNotConnectedError);
|
|
3123
|
+
}
|
|
3124
|
+
return conn.request(new FileControlH2DPacket(1, channel), FileControlReplyD2HPacket).map(() => {
|
|
3125
|
+
return;
|
|
3126
|
+
});
|
|
3853
3127
|
})());
|
|
3854
3128
|
}
|
|
3855
3129
|
}
|
|
3856
3130
|
|
|
3857
3131
|
// src/VexDevice.ts
|
|
3132
|
+
import { err as err5, ok as ok5, ResultAsync as ResultAsync5 } from "neverthrow";
|
|
3133
|
+
function unrefTimerIfPossible(timer) {
|
|
3134
|
+
if (typeof timer !== "object" || timer === null || !("unref" in timer))
|
|
3135
|
+
return;
|
|
3136
|
+
const unref = timer.unref;
|
|
3137
|
+
if (typeof unref === "function")
|
|
3138
|
+
unref.call(timer);
|
|
3139
|
+
}
|
|
3140
|
+
function describePort(port) {
|
|
3141
|
+
const info = port.getInfo();
|
|
3142
|
+
const identifier = info.path ?? info.id ?? info.serialNumber;
|
|
3143
|
+
return typeof identifier === "string" ? identifier : undefined;
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3858
3146
|
class V5SerialDevice extends VexSerialDevice {
|
|
3859
3147
|
autoReconnect = true;
|
|
3860
|
-
autoRefresh = true;
|
|
3861
3148
|
pauseRefreshOnFileTransfer = true;
|
|
3862
3149
|
_isReconnecting = false;
|
|
3863
3150
|
_isDisconnecting = false;
|
|
3864
3151
|
_refreshInterval;
|
|
3865
3152
|
state = new V5SerialDeviceState(this);
|
|
3866
3153
|
_disposed = false;
|
|
3154
|
+
_lifecycleGeneration = 0;
|
|
3155
|
+
_disconnectListener;
|
|
3867
3156
|
_refreshGeneration = 0;
|
|
3868
|
-
|
|
3157
|
+
_autoRefresh = false;
|
|
3158
|
+
_refreshIntervalMs = 200;
|
|
3159
|
+
_isLastRefreshComplete = true;
|
|
3160
|
+
_brain = new V5Brain(this.state);
|
|
3161
|
+
_controllers = [
|
|
3162
|
+
new V5Controller(this.state, 0),
|
|
3163
|
+
new V5Controller(this.state, 1)
|
|
3164
|
+
];
|
|
3165
|
+
_radio = new V5Radio(this.state);
|
|
3166
|
+
_deviceFacades = [];
|
|
3167
|
+
_emitSafely(eventName, data) {
|
|
3168
|
+
try {
|
|
3169
|
+
this.emit(eventName, data);
|
|
3170
|
+
} catch {}
|
|
3171
|
+
}
|
|
3172
|
+
constructor(defaultSerial, options = false) {
|
|
3869
3173
|
super(defaultSerial);
|
|
3870
|
-
|
|
3174
|
+
const autoRefresh = typeof options === "boolean" ? options : options.autoRefresh ?? false;
|
|
3175
|
+
this.refreshIntervalMs = typeof options === "boolean" ? 200 : options.refreshIntervalMs ?? 200;
|
|
3176
|
+
this.autoRefresh = autoRefresh;
|
|
3177
|
+
}
|
|
3178
|
+
get autoRefresh() {
|
|
3179
|
+
return this._autoRefresh;
|
|
3180
|
+
}
|
|
3181
|
+
set autoRefresh(value) {
|
|
3182
|
+
if (this._autoRefresh === value)
|
|
3183
|
+
return;
|
|
3184
|
+
this._autoRefresh = value;
|
|
3185
|
+
if (value) {
|
|
3186
|
+
this._startRefreshInterval();
|
|
3187
|
+
} else {
|
|
3188
|
+
this._stopRefreshInterval();
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
get refreshIntervalMs() {
|
|
3192
|
+
return this._refreshIntervalMs;
|
|
3193
|
+
}
|
|
3194
|
+
set refreshIntervalMs(value) {
|
|
3195
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
3196
|
+
throw new VexInvalidArgumentError("refreshIntervalMs must be a positive finite number");
|
|
3197
|
+
}
|
|
3198
|
+
if (this._refreshIntervalMs === value)
|
|
3199
|
+
return;
|
|
3200
|
+
this._refreshIntervalMs = value;
|
|
3201
|
+
if (this._refreshInterval !== undefined) {
|
|
3202
|
+
this._stopRefreshInterval();
|
|
3203
|
+
this._startRefreshInterval();
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
_startRefreshInterval() {
|
|
3207
|
+
if (this._refreshInterval !== undefined || this._disposed)
|
|
3208
|
+
return;
|
|
3871
3209
|
this._refreshInterval = setInterval(() => {
|
|
3872
3210
|
if (this._disposed)
|
|
3873
3211
|
return;
|
|
3874
|
-
if (this.
|
|
3212
|
+
if (this._autoRefresh && this._isLastRefreshComplete) {
|
|
3875
3213
|
if (!this.isConnected) {
|
|
3876
3214
|
this.state.brain.isAvailable = false;
|
|
3877
3215
|
return;
|
|
3878
3216
|
}
|
|
3879
|
-
if (!this.pauseRefreshOnFileTransfer || !this.state.
|
|
3880
|
-
|
|
3217
|
+
if (!this.pauseRefreshOnFileTransfer || !this.state.isRefreshPaused) {
|
|
3218
|
+
this._isLastRefreshComplete = false;
|
|
3881
3219
|
(async () => {
|
|
3882
3220
|
try {
|
|
3883
3221
|
const r = await this.refresh();
|
|
3884
3222
|
if (r.isErr())
|
|
3885
|
-
this.
|
|
3223
|
+
this._emitSafely("error", r.error);
|
|
3886
3224
|
} catch (error) {
|
|
3887
|
-
this.
|
|
3225
|
+
this._emitSafely("error", error);
|
|
3888
3226
|
} finally {
|
|
3889
|
-
|
|
3227
|
+
this._isLastRefreshComplete = true;
|
|
3890
3228
|
}
|
|
3891
3229
|
})();
|
|
3892
3230
|
}
|
|
3893
3231
|
}
|
|
3894
|
-
},
|
|
3232
|
+
}, this._refreshIntervalMs);
|
|
3233
|
+
unrefTimerIfPossible(this._refreshInterval);
|
|
3234
|
+
}
|
|
3235
|
+
_stopRefreshInterval() {
|
|
3236
|
+
if (this._refreshInterval === undefined)
|
|
3237
|
+
return;
|
|
3238
|
+
clearInterval(this._refreshInterval);
|
|
3239
|
+
this._refreshInterval = undefined;
|
|
3895
3240
|
}
|
|
3896
3241
|
get isV5Controller() {
|
|
3897
3242
|
return this.deviceType === 1283 /* V5_CONTROLLER */;
|
|
3898
3243
|
}
|
|
3899
3244
|
get brain() {
|
|
3900
|
-
return
|
|
3245
|
+
return this._brain;
|
|
3901
3246
|
}
|
|
3902
3247
|
get controllers() {
|
|
3903
|
-
return
|
|
3248
|
+
return this._controllers;
|
|
3904
3249
|
}
|
|
3905
3250
|
get devices() {
|
|
3906
3251
|
const rtn = [];
|
|
3907
|
-
for (let i = 1;i
|
|
3908
|
-
if (this.state.devices[i] != null)
|
|
3909
|
-
|
|
3252
|
+
for (let i = 1;i < this.state.devices.length; i++) {
|
|
3253
|
+
if (this.state.devices[i] != null) {
|
|
3254
|
+
const facade = this._deviceFacades[i] ?? new V5SmartDevice(this.state, i);
|
|
3255
|
+
this._deviceFacades[i] = facade;
|
|
3256
|
+
rtn.push(facade);
|
|
3257
|
+
}
|
|
3910
3258
|
}
|
|
3911
3259
|
return rtn;
|
|
3912
3260
|
}
|
|
@@ -3920,82 +3268,149 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3920
3268
|
this.setMatchMode(value).mapErr(() => {});
|
|
3921
3269
|
}
|
|
3922
3270
|
setMatchMode(mode) {
|
|
3923
|
-
return new
|
|
3271
|
+
return new ResultAsync5((async () => {
|
|
3924
3272
|
const reply = await this.connection?.setMatchMode(mode);
|
|
3925
3273
|
if (reply === undefined)
|
|
3926
|
-
return
|
|
3274
|
+
return err5(new VexNotConnectedError);
|
|
3927
3275
|
if (reply.isErr())
|
|
3928
|
-
return
|
|
3276
|
+
return err5(reply.error);
|
|
3929
3277
|
this.state.matchMode = mode;
|
|
3930
|
-
return
|
|
3278
|
+
return ok5(undefined);
|
|
3931
3279
|
})());
|
|
3932
3280
|
}
|
|
3933
3281
|
get radio() {
|
|
3934
|
-
return
|
|
3282
|
+
return this._radio;
|
|
3935
3283
|
}
|
|
3936
3284
|
mockTouch(x, y, press) {
|
|
3937
|
-
return new
|
|
3285
|
+
return new ResultAsync5((async () => {
|
|
3938
3286
|
const reply = await this.connection?.mockTouch(x, y, press);
|
|
3939
3287
|
if (reply === undefined)
|
|
3940
|
-
return
|
|
3288
|
+
return err5(new VexNotConnectedError);
|
|
3941
3289
|
if (reply.isErr())
|
|
3942
|
-
return
|
|
3943
|
-
return
|
|
3290
|
+
return err5(reply.error);
|
|
3291
|
+
return ok5(undefined);
|
|
3944
3292
|
})());
|
|
3945
3293
|
}
|
|
3946
3294
|
connect(conn) {
|
|
3947
|
-
|
|
3295
|
+
if (this._disposed) {
|
|
3296
|
+
return new ResultAsync5(Promise.resolve(this._staleLifecycleResult()));
|
|
3297
|
+
}
|
|
3298
|
+
if (this.isConnected)
|
|
3299
|
+
return new ResultAsync5(Promise.resolve(ok5(undefined)));
|
|
3300
|
+
const generation = ++this._lifecycleGeneration;
|
|
3301
|
+
return new ResultAsync5(this._connect(conn, generation));
|
|
3948
3302
|
}
|
|
3949
|
-
async _connect(conn) {
|
|
3303
|
+
async _connect(conn, generation = this._lifecycleGeneration) {
|
|
3304
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3305
|
+
return this._staleLifecycleResult();
|
|
3306
|
+
}
|
|
3950
3307
|
if (this.isConnected)
|
|
3951
|
-
return
|
|
3308
|
+
return ok5(undefined);
|
|
3952
3309
|
if (conn != null) {
|
|
3953
3310
|
if (!conn.isConnected) {
|
|
3954
3311
|
const opened = await conn.open();
|
|
3955
|
-
if (
|
|
3956
|
-
|
|
3312
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3313
|
+
await conn.close();
|
|
3314
|
+
return this._staleLifecycleResult();
|
|
3315
|
+
}
|
|
3316
|
+
if (opened.isErr() || opened.value !== "opened") {
|
|
3317
|
+
return err5(new VexIoError("failed to open the supplied connection"));
|
|
3957
3318
|
}
|
|
3958
3319
|
}
|
|
3959
3320
|
const q = await conn.query1();
|
|
3321
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3322
|
+
await conn.close();
|
|
3323
|
+
return this._staleLifecycleResult();
|
|
3324
|
+
}
|
|
3960
3325
|
if (q.isErr()) {
|
|
3961
3326
|
await conn.close();
|
|
3962
|
-
return
|
|
3327
|
+
return err5(q.error);
|
|
3328
|
+
}
|
|
3329
|
+
if (!this._commitConnection(conn, generation)) {
|
|
3330
|
+
await conn.close();
|
|
3331
|
+
return this._staleLifecycleResult();
|
|
3963
3332
|
}
|
|
3964
|
-
this.connection = conn;
|
|
3965
3333
|
} else {
|
|
3966
3334
|
let tryIdx = 0;
|
|
3335
|
+
let canRequestPort = true;
|
|
3336
|
+
const attemptedPorts = new Set;
|
|
3337
|
+
const attemptedPortNames = [];
|
|
3967
3338
|
while (true) {
|
|
3968
|
-
|
|
3969
|
-
|
|
3339
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3340
|
+
return this._staleLifecycleResult();
|
|
3341
|
+
}
|
|
3342
|
+
const c = this.createConnection();
|
|
3343
|
+
let result = await c.open(tryIdx++, false);
|
|
3344
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3345
|
+
await c.close();
|
|
3346
|
+
return this._staleLifecycleResult();
|
|
3347
|
+
}
|
|
3348
|
+
if (result.isOk() && result.value === "no-port" && canRequestPort) {
|
|
3349
|
+
canRequestPort = false;
|
|
3350
|
+
result = await c.open(tryIdx, true);
|
|
3351
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3352
|
+
await c.close();
|
|
3353
|
+
return this._staleLifecycleResult();
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3970
3356
|
if (result.isErr()) {
|
|
3971
3357
|
await c.close();
|
|
3972
|
-
return
|
|
3358
|
+
return err5(result.error);
|
|
3973
3359
|
}
|
|
3974
|
-
if (result.value ===
|
|
3975
|
-
|
|
3360
|
+
if (result.value === "no-port") {
|
|
3361
|
+
const attempted = attemptedPortNames.length ? `; attempted ${attemptedPortNames.join(", ")}` : "";
|
|
3362
|
+
return err5(new VexNotConnectedError(`no responsive V5 device was found${attempted}`));
|
|
3976
3363
|
}
|
|
3977
|
-
if (
|
|
3364
|
+
if (result.value === "busy") {
|
|
3978
3365
|
await c.close();
|
|
3979
|
-
|
|
3366
|
+
return err5(new VexNotConnectedError("the selected V5 serial port is busy"));
|
|
3367
|
+
}
|
|
3368
|
+
const port = c.port;
|
|
3369
|
+
if (port !== undefined && attemptedPorts.has(port)) {
|
|
3370
|
+
await c.close();
|
|
3371
|
+
const portName = describePort(port);
|
|
3372
|
+
return err5(new VexNotConnectedError(portName === undefined ? "the selected serial port did not respond as a V5 device" : `serial port ${portName} did not respond as a V5 device`));
|
|
3373
|
+
}
|
|
3374
|
+
if (port !== undefined) {
|
|
3375
|
+
attemptedPorts.add(port);
|
|
3376
|
+
const portName = describePort(port);
|
|
3377
|
+
if (portName !== undefined)
|
|
3378
|
+
attemptedPortNames.push(portName);
|
|
3980
3379
|
}
|
|
3981
3380
|
const q = await c.query1();
|
|
3381
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3382
|
+
await c.close();
|
|
3383
|
+
return this._staleLifecycleResult();
|
|
3384
|
+
}
|
|
3982
3385
|
if (q.isErr()) {
|
|
3983
3386
|
await c.close();
|
|
3984
3387
|
continue;
|
|
3985
3388
|
}
|
|
3986
|
-
this.
|
|
3389
|
+
if (!this._commitConnection(c, generation)) {
|
|
3390
|
+
await c.close();
|
|
3391
|
+
return this._staleLifecycleResult();
|
|
3392
|
+
}
|
|
3987
3393
|
break;
|
|
3988
3394
|
}
|
|
3989
3395
|
}
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3396
|
+
const connection = this.connection;
|
|
3397
|
+
if (!this._isLifecycleCurrent(generation) || connection == null) {
|
|
3398
|
+
return this._staleLifecycleResult();
|
|
3399
|
+
}
|
|
3400
|
+
if (!connection.isConnected)
|
|
3401
|
+
return err5(new VexNotConnectedError);
|
|
3402
|
+
const initialized = await this.doAfterConnect(connection, generation);
|
|
3403
|
+
if (initialized.isErr())
|
|
3404
|
+
return initialized;
|
|
3405
|
+
return ok5(undefined);
|
|
3994
3406
|
}
|
|
3995
3407
|
async disconnect() {
|
|
3408
|
+
this._lifecycleGeneration++;
|
|
3409
|
+
this._refreshGeneration++;
|
|
3996
3410
|
this._isDisconnecting = true;
|
|
3997
3411
|
const connection = this.connection;
|
|
3998
3412
|
this.connection = undefined;
|
|
3413
|
+
this._detachDisconnectListener();
|
|
3999
3414
|
try {
|
|
4000
3415
|
await connection?.close();
|
|
4001
3416
|
} finally {
|
|
@@ -4006,20 +3421,28 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4006
3421
|
this.autoReconnect = false;
|
|
4007
3422
|
this.autoRefresh = false;
|
|
4008
3423
|
this._disposed = true;
|
|
4009
|
-
if (this._refreshInterval !== undefined) {
|
|
4010
|
-
clearInterval(this._refreshInterval);
|
|
4011
|
-
this._refreshInterval = undefined;
|
|
4012
|
-
}
|
|
4013
3424
|
await this.disconnect();
|
|
4014
3425
|
}
|
|
4015
3426
|
reconnect(timeout = 0) {
|
|
4016
|
-
|
|
3427
|
+
if (this._disposed) {
|
|
3428
|
+
return new ResultAsync5(Promise.resolve(this._staleLifecycleResult()));
|
|
3429
|
+
}
|
|
3430
|
+
if (timeout < 0) {
|
|
3431
|
+
return new ResultAsync5(Promise.resolve(err5(new VexInvalidArgumentError("timeout must be non-negative"))));
|
|
3432
|
+
}
|
|
3433
|
+
if (this.isConnected)
|
|
3434
|
+
return new ResultAsync5(Promise.resolve(ok5(undefined)));
|
|
3435
|
+
const generation = this._isReconnecting ? this._lifecycleGeneration : ++this._lifecycleGeneration;
|
|
3436
|
+
return new ResultAsync5(this._reconnect(timeout, generation));
|
|
4017
3437
|
}
|
|
4018
|
-
async _reconnect(timeout) {
|
|
3438
|
+
async _reconnect(timeout, generation = this._lifecycleGeneration) {
|
|
3439
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3440
|
+
return this._staleLifecycleResult();
|
|
3441
|
+
}
|
|
4019
3442
|
if (this.isConnected)
|
|
4020
|
-
return
|
|
3443
|
+
return ok5(undefined);
|
|
4021
3444
|
if (timeout < 0) {
|
|
4022
|
-
return
|
|
3445
|
+
return err5(new VexInvalidArgumentError("timeout must be non-negative"));
|
|
4023
3446
|
}
|
|
4024
3447
|
const endTime = Date.now() + timeout;
|
|
4025
3448
|
if (this._isReconnecting) {
|
|
@@ -4028,30 +3451,44 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4028
3451
|
} else {
|
|
4029
3452
|
const waited = await sleepUntil(() => !this._isReconnecting, timeout);
|
|
4030
3453
|
if (waited.isErr() || !waited.value) {
|
|
4031
|
-
return
|
|
3454
|
+
return err5(new VexNotConnectedError);
|
|
4032
3455
|
}
|
|
4033
3456
|
}
|
|
3457
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3458
|
+
return this._staleLifecycleResult();
|
|
3459
|
+
}
|
|
4034
3460
|
if (this.isConnected)
|
|
4035
|
-
return
|
|
3461
|
+
return ok5(undefined);
|
|
4036
3462
|
}
|
|
4037
3463
|
this._isReconnecting = true;
|
|
4038
3464
|
try {
|
|
4039
3465
|
while (timeout === 0 || Date.now() < endTime) {
|
|
4040
3466
|
let tryIdx = 0;
|
|
4041
3467
|
while (true) {
|
|
4042
|
-
|
|
3468
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3469
|
+
return this._staleLifecycleResult();
|
|
3470
|
+
}
|
|
3471
|
+
const c = this.createConnection();
|
|
4043
3472
|
const result = await c.open(tryIdx++, false);
|
|
3473
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3474
|
+
await c.close();
|
|
3475
|
+
return this._staleLifecycleResult();
|
|
3476
|
+
}
|
|
4044
3477
|
if (result.isErr()) {
|
|
4045
3478
|
await c.close();
|
|
4046
|
-
return
|
|
3479
|
+
return err5(result.error);
|
|
4047
3480
|
}
|
|
4048
|
-
if (result.value ===
|
|
3481
|
+
if (result.value === "no-port")
|
|
4049
3482
|
break;
|
|
4050
|
-
if (
|
|
3483
|
+
if (result.value === "busy") {
|
|
4051
3484
|
await c.close();
|
|
4052
3485
|
continue;
|
|
4053
3486
|
}
|
|
4054
3487
|
const status = await c.getSystemStatus(200);
|
|
3488
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3489
|
+
await c.close();
|
|
3490
|
+
return this._staleLifecycleResult();
|
|
3491
|
+
}
|
|
4055
3492
|
if (status.isErr()) {
|
|
4056
3493
|
await c.close();
|
|
4057
3494
|
continue;
|
|
@@ -4060,24 +3497,39 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4060
3497
|
await c.close();
|
|
4061
3498
|
continue;
|
|
4062
3499
|
}
|
|
4063
|
-
this.
|
|
3500
|
+
if (!this._commitConnection(c, generation)) {
|
|
3501
|
+
await c.close();
|
|
3502
|
+
return this._staleLifecycleResult();
|
|
3503
|
+
}
|
|
4064
3504
|
break;
|
|
4065
3505
|
}
|
|
4066
3506
|
if (this.isConnected)
|
|
4067
3507
|
break;
|
|
4068
3508
|
const getPortCount = async () => (await this.defaultSerial.getPorts()).length;
|
|
4069
3509
|
const portsCount = await getPortCount();
|
|
3510
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3511
|
+
return this._staleLifecycleResult();
|
|
3512
|
+
}
|
|
4070
3513
|
await sleepUntilAsync(async () => await getPortCount() !== portsCount, 1000);
|
|
3514
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3515
|
+
return this._staleLifecycleResult();
|
|
3516
|
+
}
|
|
4071
3517
|
}
|
|
4072
3518
|
} catch (e) {
|
|
4073
|
-
return
|
|
3519
|
+
return err5(toVexSerialError(e));
|
|
4074
3520
|
} finally {
|
|
4075
3521
|
this._isReconnecting = false;
|
|
4076
3522
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
3523
|
+
const connection = this.connection;
|
|
3524
|
+
if (!this._isLifecycleCurrent(generation) || connection == null) {
|
|
3525
|
+
return this._staleLifecycleResult();
|
|
3526
|
+
}
|
|
3527
|
+
if (!connection.isConnected)
|
|
3528
|
+
return err5(new VexNotConnectedError);
|
|
3529
|
+
const initialized = await this.doAfterConnect(connection, generation);
|
|
3530
|
+
if (initialized.isErr())
|
|
3531
|
+
return initialized;
|
|
3532
|
+
return ok5(undefined);
|
|
4081
3533
|
}
|
|
4082
3534
|
async waitForReconnectToFinish() {
|
|
4083
3535
|
while (this._isReconnecting) {
|
|
@@ -4086,58 +3538,96 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4086
3538
|
return;
|
|
4087
3539
|
}
|
|
4088
3540
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
3541
|
+
createConnection() {
|
|
3542
|
+
return new V5SerialConnection(this.defaultSerial);
|
|
3543
|
+
}
|
|
3544
|
+
_isLifecycleCurrent(generation) {
|
|
3545
|
+
return generation === this._lifecycleGeneration && !this._disposed;
|
|
3546
|
+
}
|
|
3547
|
+
_staleLifecycleResult() {
|
|
3548
|
+
return err5(new VexNotConnectedError("connection attempt was superseded"));
|
|
3549
|
+
}
|
|
3550
|
+
_commitConnection(connection, generation) {
|
|
3551
|
+
if (!this._isLifecycleCurrent(generation))
|
|
3552
|
+
return false;
|
|
3553
|
+
this._detachDisconnectListener();
|
|
3554
|
+
this.connection = connection;
|
|
3555
|
+
return true;
|
|
3556
|
+
}
|
|
3557
|
+
_detachDisconnectListener() {
|
|
3558
|
+
const subscription = this._disconnectListener;
|
|
3559
|
+
this._disconnectListener = undefined;
|
|
3560
|
+
subscription?.connection.remove("disconnected", subscription.listener);
|
|
3561
|
+
}
|
|
3562
|
+
async doAfterConnect(connection, generation) {
|
|
3563
|
+
if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
|
|
3564
|
+
return this._staleLifecycleResult();
|
|
3565
|
+
}
|
|
3566
|
+
const listener = () => {
|
|
3567
|
+
if (this._isDisconnecting || !this._isLifecycleCurrent(generation) || this.connection !== connection) {
|
|
3568
|
+
return;
|
|
4095
3569
|
}
|
|
4096
|
-
|
|
4097
|
-
|
|
3570
|
+
this._lifecycleGeneration++;
|
|
3571
|
+
this._refreshGeneration++;
|
|
3572
|
+
this.connection = undefined;
|
|
3573
|
+
this._detachDisconnectListener();
|
|
3574
|
+
this._emitSafely("disconnected", undefined);
|
|
3575
|
+
if (this.autoReconnect && !this._disposed) {
|
|
3576
|
+
this.reconnect().mapErr((error) => this._emitSafely("error", error));
|
|
3577
|
+
}
|
|
3578
|
+
};
|
|
3579
|
+
connection.on("disconnected", listener);
|
|
3580
|
+
this._disconnectListener = { connection, listener };
|
|
3581
|
+
const refreshed = await this.refresh();
|
|
3582
|
+
if (!this._isLifecycleCurrent(generation) || this.connection !== connection || !connection.isConnected) {
|
|
3583
|
+
return this._staleLifecycleResult();
|
|
3584
|
+
}
|
|
3585
|
+
if (refreshed.isErr()) {
|
|
3586
|
+
await this._discardConnection(connection, generation);
|
|
3587
|
+
return err5(refreshed.error);
|
|
3588
|
+
}
|
|
3589
|
+
if (!refreshed.value) {
|
|
3590
|
+
await this._discardConnection(connection, generation);
|
|
3591
|
+
return err5(new VexNotConnectedError("initial device refresh did not produce a current snapshot"));
|
|
3592
|
+
}
|
|
3593
|
+
return ok5(undefined);
|
|
3594
|
+
}
|
|
3595
|
+
async _discardConnection(connection, generation) {
|
|
3596
|
+
if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3599
|
+
this._lifecycleGeneration++;
|
|
3600
|
+
this._refreshGeneration++;
|
|
3601
|
+
this.connection = undefined;
|
|
3602
|
+
this._detachDisconnectListener();
|
|
3603
|
+
await connection.close();
|
|
4098
3604
|
}
|
|
4099
3605
|
refresh() {
|
|
4100
|
-
return new
|
|
3606
|
+
return new ResultAsync5(this._refresh());
|
|
4101
3607
|
}
|
|
4102
3608
|
async _refresh() {
|
|
4103
3609
|
if (this._disposed)
|
|
4104
|
-
return
|
|
3610
|
+
return ok5(false);
|
|
4105
3611
|
const generation = ++this._refreshGeneration;
|
|
4106
3612
|
const conn = this.connection;
|
|
4107
3613
|
if (conn == null || !conn.isConnected) {
|
|
4108
3614
|
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
4109
|
-
return
|
|
4110
|
-
}
|
|
4111
|
-
const ssPacket = await
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
}
|
|
4118
|
-
const sfPacket = await conn.getSystemFlags();
|
|
4119
|
-
if (generation !== this._refreshGeneration || this._disposed)
|
|
4120
|
-
return ok(false);
|
|
4121
|
-
if (sfPacket.isErr()) {
|
|
4122
|
-
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
4123
|
-
return ok(false);
|
|
4124
|
-
}
|
|
4125
|
-
const rdPacket = await conn.getRadioStatus();
|
|
4126
|
-
if (generation !== this._refreshGeneration || this._disposed)
|
|
4127
|
-
return ok(false);
|
|
4128
|
-
if (rdPacket.isErr()) {
|
|
4129
|
-
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
4130
|
-
return ok(false);
|
|
4131
|
-
}
|
|
4132
|
-
const dsPacket = await conn.getDeviceStatus();
|
|
3615
|
+
return ok5(false);
|
|
3616
|
+
}
|
|
3617
|
+
const [ssPacket, sfPacket, rdPacket, dsPacket] = await Promise.all([
|
|
3618
|
+
conn.getSystemStatus(),
|
|
3619
|
+
conn.getSystemFlags(),
|
|
3620
|
+
conn.getRadioStatus(),
|
|
3621
|
+
conn.getDeviceStatus()
|
|
3622
|
+
]);
|
|
4133
3623
|
if (generation !== this._refreshGeneration || this._disposed)
|
|
4134
|
-
return
|
|
4135
|
-
if (dsPacket.isErr()) {
|
|
3624
|
+
return ok5(false);
|
|
3625
|
+
if (ssPacket.isErr() || sfPacket.isErr() || rdPacket.isErr() || dsPacket.isErr()) {
|
|
4136
3626
|
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
4137
|
-
return
|
|
3627
|
+
return ok5(false);
|
|
4138
3628
|
}
|
|
4139
3629
|
const snapshot = this._buildSnapshot(ssPacket.value, sfPacket.value, rdPacket.value, dsPacket.value);
|
|
4140
|
-
return
|
|
3630
|
+
return ok5(this._applySnapshotIfCurrent(generation, snapshot));
|
|
4141
3631
|
}
|
|
4142
3632
|
_buildSnapshot(ssPacket, sfPacket, rdPacket, dsPacket) {
|
|
4143
3633
|
const flags2 = ssPacket.sysflags[2];
|
|
@@ -4188,7 +3678,8 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4188
3678
|
},
|
|
4189
3679
|
{
|
|
4190
3680
|
battery: controller1Battery,
|
|
4191
|
-
isAvailable: controller1Available
|
|
3681
|
+
isAvailable: controller1Available,
|
|
3682
|
+
isCharging: undefined
|
|
4192
3683
|
}
|
|
4193
3684
|
],
|
|
4194
3685
|
radio: {
|
|
@@ -4215,7 +3706,26 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4215
3706
|
}
|
|
4216
3707
|
this.state.matchMode = snapshot.matchMode;
|
|
4217
3708
|
this.state.isFieldControllerConnected = snapshot.isFieldControllerConnected;
|
|
4218
|
-
|
|
3709
|
+
const brain = this.state.brain;
|
|
3710
|
+
brain.activeProgram = snapshot.brain.activeProgram;
|
|
3711
|
+
brain.battery.batteryPercent = snapshot.brain.battery.batteryPercent;
|
|
3712
|
+
brain.battery.isCharging = snapshot.brain.battery.isCharging;
|
|
3713
|
+
brain.button.isPressed = snapshot.brain.button.isPressed;
|
|
3714
|
+
brain.button.isDoublePressed = snapshot.brain.button.isDoublePressed;
|
|
3715
|
+
if (brain.cpu0Version.compare(snapshot.brain.cpu0Version) !== 0) {
|
|
3716
|
+
brain.cpu0Version = snapshot.brain.cpu0Version;
|
|
3717
|
+
}
|
|
3718
|
+
if (brain.cpu1Version.compare(snapshot.brain.cpu1Version) !== 0) {
|
|
3719
|
+
brain.cpu1Version = snapshot.brain.cpu1Version;
|
|
3720
|
+
}
|
|
3721
|
+
brain.isAvailable = snapshot.brain.isAvailable;
|
|
3722
|
+
brain.settings.isScreenReversed = snapshot.brain.settings.isScreenReversed;
|
|
3723
|
+
brain.settings.isWhiteTheme = snapshot.brain.settings.isWhiteTheme;
|
|
3724
|
+
brain.settings.usingLanguage = snapshot.brain.settings.usingLanguage;
|
|
3725
|
+
if (brain.systemVersion.compare(snapshot.brain.systemVersion) !== 0) {
|
|
3726
|
+
brain.systemVersion = snapshot.brain.systemVersion;
|
|
3727
|
+
}
|
|
3728
|
+
brain.uniqueId = snapshot.brain.uniqueId;
|
|
4219
3729
|
Object.assign(this.state.controllers[0], snapshot.controllers[0]);
|
|
4220
3730
|
Object.assign(this.state.controllers[1], snapshot.controllers[1]);
|
|
4221
3731
|
Object.assign(this.state.radio, snapshot.radio);
|
|
@@ -4224,10 +3734,18 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
4224
3734
|
if (device != null)
|
|
4225
3735
|
next[device.port] = device;
|
|
4226
3736
|
}
|
|
4227
|
-
this.state.devices
|
|
3737
|
+
if (!sameSmartDeviceSlots(this.state.devices, next)) {
|
|
3738
|
+
this.state.devices = next;
|
|
3739
|
+
}
|
|
4228
3740
|
return true;
|
|
4229
3741
|
}
|
|
4230
3742
|
}
|
|
3743
|
+
function sameSmartDeviceSlots(left, right) {
|
|
3744
|
+
return left.length === right.length && left.every((device, index) => {
|
|
3745
|
+
const next = right[index];
|
|
3746
|
+
return device === next || device !== undefined && next !== undefined && device.port === next.port && device.type === next.type && device.status === next.status && device.betaversion === next.betaversion && device.version === next.version && device.bootversion === next.bootversion;
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
4231
3749
|
// src/VexIniConfig.ts
|
|
4232
3750
|
class BaseIniBuilder {
|
|
4233
3751
|
str = "";
|
|
@@ -4236,7 +3754,7 @@ class BaseIniBuilder {
|
|
|
4236
3754
|
`;
|
|
4237
3755
|
}
|
|
4238
3756
|
addComment(comment) {
|
|
4239
|
-
this.addLine("; " + comment);
|
|
3757
|
+
this.addLine(comment.length === 0 ? ";" : "; " + comment);
|
|
4240
3758
|
return this;
|
|
4241
3759
|
}
|
|
4242
3760
|
getContent() {
|
|
@@ -4254,24 +3772,28 @@ class IniSectionBuilder extends BaseIniBuilder {
|
|
|
4254
3772
|
this.object = object;
|
|
4255
3773
|
this.keyTransform = keyTransform;
|
|
4256
3774
|
}
|
|
4257
|
-
addSingleObjProperty(key, maxValueLength) {
|
|
4258
|
-
|
|
3775
|
+
addSingleObjProperty(key, maxValueLength, omitIfEmpty = false) {
|
|
3776
|
+
const rawValue = this.object[key];
|
|
3777
|
+
if (rawValue == null)
|
|
3778
|
+
return;
|
|
3779
|
+
const value = rawValue.toString();
|
|
3780
|
+
if (omitIfEmpty && value.length === 0)
|
|
4259
3781
|
return;
|
|
3782
|
+
if (/["\u0000-\u001f\u007f]/.test(value)) {
|
|
3783
|
+
throw new Error(`INI value for ${this.name}.${String(key)} contains an unsupported character`);
|
|
3784
|
+
}
|
|
4260
3785
|
const formattedKey = this.keyTransform(key).padEnd(12).slice(0, 12);
|
|
4261
|
-
const
|
|
4262
|
-
|
|
4263
|
-
this.addLine(`${formattedKey} = "${escapedVal}"`);
|
|
3786
|
+
const trimmedValue = value.slice(0, maxValueLength);
|
|
3787
|
+
this.addLine(`${formattedKey} = "${trimmedValue}"`);
|
|
4264
3788
|
}
|
|
4265
|
-
addObjProperty(key, maxValueLength) {
|
|
4266
|
-
const
|
|
4267
|
-
|
|
4268
|
-
this.addSingleObjProperty(k, maxValueLength);
|
|
3789
|
+
addObjProperty(key, maxValueLength, omitIfEmpty = false) {
|
|
3790
|
+
for (const k of Array.isArray(key) ? key : [key]) {
|
|
3791
|
+
this.addSingleObjProperty(k, maxValueLength, omitIfEmpty);
|
|
4269
3792
|
}
|
|
4270
3793
|
return this;
|
|
4271
3794
|
}
|
|
4272
3795
|
addAllObjProps(maxValueLength) {
|
|
4273
|
-
const
|
|
4274
|
-
for (const k of keys) {
|
|
3796
|
+
for (const k of Object.keys(this.object)) {
|
|
4275
3797
|
this.addSingleObjProperty(k, maxValueLength);
|
|
4276
3798
|
}
|
|
4277
3799
|
return this;
|
|
@@ -4284,6 +3806,11 @@ class IniFileBuilder extends BaseIniBuilder {
|
|
|
4284
3806
|
this.str += section.getContent();
|
|
4285
3807
|
return this;
|
|
4286
3808
|
}
|
|
3809
|
+
addOptionalSection(section) {
|
|
3810
|
+
if (section.getContent().length === 0)
|
|
3811
|
+
return this;
|
|
3812
|
+
return this.addComment("").addSection(section);
|
|
3813
|
+
}
|
|
4287
3814
|
}
|
|
4288
3815
|
|
|
4289
3816
|
class ProgramIniConfig {
|
|
@@ -4300,31 +3827,24 @@ class ProgramIniConfig {
|
|
|
4300
3827
|
date: "",
|
|
4301
3828
|
timezone: "0"
|
|
4302
3829
|
};
|
|
4303
|
-
config = {};
|
|
3830
|
+
config = { 22: "adi" };
|
|
4304
3831
|
controller1 = {};
|
|
4305
3832
|
controller2 = {};
|
|
4306
|
-
constructor() {
|
|
4307
|
-
this.config = {
|
|
4308
|
-
22: "adi"
|
|
4309
|
-
};
|
|
4310
|
-
}
|
|
4311
3833
|
setProgramDate(date) {
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
const
|
|
4315
|
-
const
|
|
4316
|
-
|
|
4317
|
-
this.program.timezone = (d.getTimezoneOffset() > 0 ? "-" : "+") + this.dec2(tzh) + ":" + this.dec2(tzm);
|
|
3834
|
+
this.program.date = date.toISOString();
|
|
3835
|
+
const offset = Math.abs(date.getTimezoneOffset());
|
|
3836
|
+
const hours = offset / 60 >>> 0;
|
|
3837
|
+
const minutes = offset - hours * 60;
|
|
3838
|
+
this.program.timezone = (date.getTimezoneOffset() > 0 ? "-" : "+") + this.dec2(hours) + ":" + this.dec2(minutes);
|
|
4318
3839
|
}
|
|
4319
3840
|
createIni() {
|
|
4320
3841
|
if (this.program.date.length === 0) {
|
|
4321
3842
|
this.setProgramDate(new Date);
|
|
4322
3843
|
}
|
|
4323
|
-
return new IniFileBuilder().addComment("").addComment("VEX program ini file").addComment("Generated by Vex V5 Serial Protocol Library").addComment("").addSection(new IniSectionBuilder("project", this.project).addObjProperty("ide", 16)).addComment("").addSection(new IniSectionBuilder("program", this.program).addObjProperty("name", 32).addObjProperty("
|
|
3844
|
+
return new IniFileBuilder().addComment("").addComment("VEX program ini file").addComment("Generated by Vex V5 Serial Protocol Library").addComment("").addSection(new IniSectionBuilder("project", this.project).addObjProperty("version").addObjProperty("ide", 16).addObjProperty("file")).addComment("").addSection(new IniSectionBuilder("program", this.program).addObjProperty("version").addObjProperty("name", 32).addObjProperty("slot").addObjProperty("icon", 16).addObjProperty("iconalt", 16, true).addObjProperty("description", 256).addObjProperty("date").addObjProperty("timezone")).addComment("").addSection(new IniSectionBuilder("config", this.config, (k) => "port_" + this.dec2(k)).addAllObjProps()).addOptionalSection(new IniSectionBuilder("controller_1", this.controller1).addAllObjProps()).addOptionalSection(new IniSectionBuilder("controller_2", this.controller2).addAllObjProps()).getContent();
|
|
4324
3845
|
}
|
|
4325
3846
|
dec2(value) {
|
|
4326
|
-
|
|
4327
|
-
return str.toUpperCase();
|
|
3847
|
+
return (value % 100).toString(10).padStart(2, "0").toUpperCase();
|
|
4328
3848
|
}
|
|
4329
3849
|
}
|
|
4330
3850
|
export {
|
|
@@ -4456,5 +3976,5 @@ export {
|
|
|
4456
3976
|
AckType
|
|
4457
3977
|
};
|
|
4458
3978
|
|
|
4459
|
-
//# debugId=
|
|
3979
|
+
//# debugId=048E789F43ADDE3C64756E2164756E21
|
|
4460
3980
|
//# sourceMappingURL=index.js.map
|