@v5x/serial 0.5.6 → 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 +37 -4
- package/dist/Vex.d.ts +3 -1
- package/dist/VexConnection.d.ts +48 -3
- package/dist/VexDevice.d.ts +22 -1
- package/dist/VexDeviceState.d.ts +18 -5
- package/dist/VexError.d.ts +1 -0
- package/dist/VexEvent.d.ts +0 -1
- package/dist/VexFirmware.d.ts +1 -0
- package/dist/VexPacketCore.d.ts +5 -0
- package/dist/VexPacketEncoder.d.ts +2 -0
- package/dist/VexPacketModels.d.ts +1 -1
- package/dist/VexPacketRegistry.d.ts +3 -0
- package/dist/VexScreenCapture.d.ts +2 -0
- package/dist/VexTransfers.d.ts +1 -0
- package/dist/index.cjs +802 -963
- package/dist/index.cjs.map +14 -14
- package/dist/index.js +898 -1011
- package/dist/index.js.map +14 -14
- 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) => {
|
|
@@ -287,8 +274,15 @@ class VexEventEmitter {
|
|
|
287
274
|
this.handlerMap.set(eventName, listeners);
|
|
288
275
|
}
|
|
289
276
|
emit(eventName, data) {
|
|
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
|
+
}
|
|
290
284
|
const errors = [];
|
|
291
|
-
for (const callback of [...
|
|
285
|
+
for (const callback of [...listeners]) {
|
|
292
286
|
try {
|
|
293
287
|
callback(data);
|
|
294
288
|
} catch (error) {
|
|
@@ -312,472 +306,21 @@ class VexEventTarget {
|
|
|
312
306
|
this.emitter = new VexEventEmitter;
|
|
313
307
|
}
|
|
314
308
|
emit(eventName, data) {
|
|
315
|
-
this.emitter.emit(
|
|
309
|
+
this.emitter.emit(eventName, data);
|
|
316
310
|
}
|
|
317
311
|
on(eventName, listener) {
|
|
318
|
-
this.emitter.on(
|
|
312
|
+
this.emitter.on(eventName, listener);
|
|
319
313
|
}
|
|
320
314
|
remove(eventName, listener) {
|
|
321
|
-
this.emitter.remove(
|
|
315
|
+
this.emitter.remove(eventName, listener);
|
|
322
316
|
}
|
|
323
317
|
clearListeners() {
|
|
324
318
|
this.emitter.clearListeners();
|
|
325
319
|
}
|
|
326
|
-
normalizeEventName(eventName) {
|
|
327
|
-
return String(eventName);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ../../node_modules/.bun/neverthrow@8.2.0/node_modules/neverthrow/dist/index.es.js
|
|
332
|
-
var defaultErrorConfig = {
|
|
333
|
-
withStackTrace: false
|
|
334
|
-
};
|
|
335
|
-
var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
|
|
336
|
-
const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
|
|
337
|
-
const maybeStack = config.withStackTrace ? new Error().stack : undefined;
|
|
338
|
-
return {
|
|
339
|
-
data,
|
|
340
|
-
message,
|
|
341
|
-
stack: maybeStack
|
|
342
|
-
};
|
|
343
|
-
};
|
|
344
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
345
|
-
function adopt(value) {
|
|
346
|
-
return value instanceof P ? value : new P(function(resolve) {
|
|
347
|
-
resolve(value);
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
return new (P || (P = Promise))(function(resolve, reject) {
|
|
351
|
-
function fulfilled(value) {
|
|
352
|
-
try {
|
|
353
|
-
step(generator.next(value));
|
|
354
|
-
} catch (e) {
|
|
355
|
-
reject(e);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
function rejected(value) {
|
|
359
|
-
try {
|
|
360
|
-
step(generator["throw"](value));
|
|
361
|
-
} catch (e) {
|
|
362
|
-
reject(e);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
function step(result) {
|
|
366
|
-
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
367
|
-
}
|
|
368
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
function __values(o) {
|
|
372
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
373
|
-
if (m)
|
|
374
|
-
return m.call(o);
|
|
375
|
-
if (o && typeof o.length === "number")
|
|
376
|
-
return {
|
|
377
|
-
next: function() {
|
|
378
|
-
if (o && i >= o.length)
|
|
379
|
-
o = undefined;
|
|
380
|
-
return { value: o && o[i++], done: !o };
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
384
|
-
}
|
|
385
|
-
function __await(v) {
|
|
386
|
-
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
387
|
-
}
|
|
388
|
-
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
389
|
-
if (!Symbol.asyncIterator)
|
|
390
|
-
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
391
|
-
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
392
|
-
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
|
|
393
|
-
return this;
|
|
394
|
-
}, i;
|
|
395
|
-
function awaitReturn(f) {
|
|
396
|
-
return function(v) {
|
|
397
|
-
return Promise.resolve(v).then(f, reject);
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
function verb(n, f) {
|
|
401
|
-
if (g[n]) {
|
|
402
|
-
i[n] = function(v) {
|
|
403
|
-
return new Promise(function(a, b) {
|
|
404
|
-
q.push([n, v, a, b]) > 1 || resume(n, v);
|
|
405
|
-
});
|
|
406
|
-
};
|
|
407
|
-
if (f)
|
|
408
|
-
i[n] = f(i[n]);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
function resume(n, v) {
|
|
412
|
-
try {
|
|
413
|
-
step(g[n](v));
|
|
414
|
-
} catch (e) {
|
|
415
|
-
settle(q[0][3], e);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
function step(r) {
|
|
419
|
-
r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
|
|
420
|
-
}
|
|
421
|
-
function fulfill(value) {
|
|
422
|
-
resume("next", value);
|
|
423
|
-
}
|
|
424
|
-
function reject(value) {
|
|
425
|
-
resume("throw", value);
|
|
426
|
-
}
|
|
427
|
-
function settle(f, v) {
|
|
428
|
-
if (f(v), q.shift(), q.length)
|
|
429
|
-
resume(q[0][0], q[0][1]);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
function __asyncDelegator(o) {
|
|
433
|
-
var i, p;
|
|
434
|
-
return i = {}, verb("next"), verb("throw", function(e) {
|
|
435
|
-
throw e;
|
|
436
|
-
}), verb("return"), i[Symbol.iterator] = function() {
|
|
437
|
-
return this;
|
|
438
|
-
}, i;
|
|
439
|
-
function verb(n, f) {
|
|
440
|
-
i[n] = o[n] ? function(v) {
|
|
441
|
-
return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
|
|
442
|
-
} : f;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
function __asyncValues(o) {
|
|
446
|
-
if (!Symbol.asyncIterator)
|
|
447
|
-
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
448
|
-
var m = o[Symbol.asyncIterator], i;
|
|
449
|
-
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() {
|
|
450
|
-
return this;
|
|
451
|
-
}, i);
|
|
452
|
-
function verb(n) {
|
|
453
|
-
i[n] = o[n] && function(v) {
|
|
454
|
-
return new Promise(function(resolve, reject) {
|
|
455
|
-
v = o[n](v), settle(resolve, reject, v.done, v.value);
|
|
456
|
-
});
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
function settle(resolve, reject, d, v) {
|
|
460
|
-
Promise.resolve(v).then(function(v2) {
|
|
461
|
-
resolve({ value: v2, done: d });
|
|
462
|
-
}, reject);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
class ResultAsync {
|
|
466
|
-
constructor(res) {
|
|
467
|
-
this._promise = res;
|
|
468
|
-
}
|
|
469
|
-
static fromSafePromise(promise) {
|
|
470
|
-
const newPromise = promise.then((value) => new Ok(value));
|
|
471
|
-
return new ResultAsync(newPromise);
|
|
472
|
-
}
|
|
473
|
-
static fromPromise(promise, errorFn) {
|
|
474
|
-
const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
|
|
475
|
-
return new ResultAsync(newPromise);
|
|
476
|
-
}
|
|
477
|
-
static fromThrowable(fn, errorFn) {
|
|
478
|
-
return (...args) => {
|
|
479
|
-
return new ResultAsync((() => __awaiter(this, undefined, undefined, function* () {
|
|
480
|
-
try {
|
|
481
|
-
return new Ok(yield fn(...args));
|
|
482
|
-
} catch (error) {
|
|
483
|
-
return new Err(errorFn ? errorFn(error) : error);
|
|
484
|
-
}
|
|
485
|
-
}))());
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
static combine(asyncResultList) {
|
|
489
|
-
return combineResultAsyncList(asyncResultList);
|
|
490
|
-
}
|
|
491
|
-
static combineWithAllErrors(asyncResultList) {
|
|
492
|
-
return combineResultAsyncListWithAllErrors(asyncResultList);
|
|
493
|
-
}
|
|
494
|
-
map(f) {
|
|
495
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
496
|
-
if (res.isErr()) {
|
|
497
|
-
return new Err(res.error);
|
|
498
|
-
}
|
|
499
|
-
return new Ok(yield f(res.value));
|
|
500
|
-
})));
|
|
501
|
-
}
|
|
502
|
-
andThrough(f) {
|
|
503
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
504
|
-
if (res.isErr()) {
|
|
505
|
-
return new Err(res.error);
|
|
506
|
-
}
|
|
507
|
-
const newRes = yield f(res.value);
|
|
508
|
-
if (newRes.isErr()) {
|
|
509
|
-
return new Err(newRes.error);
|
|
510
|
-
}
|
|
511
|
-
return new Ok(res.value);
|
|
512
|
-
})));
|
|
513
|
-
}
|
|
514
|
-
andTee(f) {
|
|
515
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
516
|
-
if (res.isErr()) {
|
|
517
|
-
return new Err(res.error);
|
|
518
|
-
}
|
|
519
|
-
try {
|
|
520
|
-
yield f(res.value);
|
|
521
|
-
} catch (e) {}
|
|
522
|
-
return new Ok(res.value);
|
|
523
|
-
})));
|
|
524
|
-
}
|
|
525
|
-
orTee(f) {
|
|
526
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
527
|
-
if (res.isOk()) {
|
|
528
|
-
return new Ok(res.value);
|
|
529
|
-
}
|
|
530
|
-
try {
|
|
531
|
-
yield f(res.error);
|
|
532
|
-
} catch (e) {}
|
|
533
|
-
return new Err(res.error);
|
|
534
|
-
})));
|
|
535
|
-
}
|
|
536
|
-
mapErr(f) {
|
|
537
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
538
|
-
if (res.isOk()) {
|
|
539
|
-
return new Ok(res.value);
|
|
540
|
-
}
|
|
541
|
-
return new Err(yield f(res.error));
|
|
542
|
-
})));
|
|
543
|
-
}
|
|
544
|
-
andThen(f) {
|
|
545
|
-
return new ResultAsync(this._promise.then((res) => {
|
|
546
|
-
if (res.isErr()) {
|
|
547
|
-
return new Err(res.error);
|
|
548
|
-
}
|
|
549
|
-
const newValue = f(res.value);
|
|
550
|
-
return newValue instanceof ResultAsync ? newValue._promise : newValue;
|
|
551
|
-
}));
|
|
552
|
-
}
|
|
553
|
-
orElse(f) {
|
|
554
|
-
return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
|
|
555
|
-
if (res.isErr()) {
|
|
556
|
-
return f(res.error);
|
|
557
|
-
}
|
|
558
|
-
return new Ok(res.value);
|
|
559
|
-
})));
|
|
560
|
-
}
|
|
561
|
-
match(ok, _err) {
|
|
562
|
-
return this._promise.then((res) => res.match(ok, _err));
|
|
563
|
-
}
|
|
564
|
-
unwrapOr(t) {
|
|
565
|
-
return this._promise.then((res) => res.unwrapOr(t));
|
|
566
|
-
}
|
|
567
|
-
safeUnwrap() {
|
|
568
|
-
return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
|
|
569
|
-
return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
then(successCallback, failureCallback) {
|
|
573
|
-
return this._promise.then(successCallback, failureCallback);
|
|
574
|
-
}
|
|
575
|
-
[Symbol.asyncIterator]() {
|
|
576
|
-
return __asyncGenerator(this, arguments, function* _a() {
|
|
577
|
-
const result = yield __await(this._promise);
|
|
578
|
-
if (result.isErr()) {
|
|
579
|
-
yield yield __await(errAsync(result.error));
|
|
580
|
-
}
|
|
581
|
-
return yield __await(result.value);
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
function errAsync(err) {
|
|
586
|
-
return new ResultAsync(Promise.resolve(new Err(err)));
|
|
587
|
-
}
|
|
588
|
-
var fromPromise = ResultAsync.fromPromise;
|
|
589
|
-
var fromSafePromise = ResultAsync.fromSafePromise;
|
|
590
|
-
var fromAsyncThrowable = ResultAsync.fromThrowable;
|
|
591
|
-
var combineResultList = (resultList) => {
|
|
592
|
-
let acc = ok([]);
|
|
593
|
-
for (const result of resultList) {
|
|
594
|
-
if (result.isErr()) {
|
|
595
|
-
acc = err(result.error);
|
|
596
|
-
break;
|
|
597
|
-
} else {
|
|
598
|
-
acc.map((list) => list.push(result.value));
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
return acc;
|
|
602
|
-
};
|
|
603
|
-
var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
|
|
604
|
-
var combineResultListWithAllErrors = (resultList) => {
|
|
605
|
-
let acc = ok([]);
|
|
606
|
-
for (const result of resultList) {
|
|
607
|
-
if (result.isErr() && acc.isErr()) {
|
|
608
|
-
acc.error.push(result.error);
|
|
609
|
-
} else if (result.isErr() && acc.isOk()) {
|
|
610
|
-
acc = err([result.error]);
|
|
611
|
-
} else if (result.isOk() && acc.isOk()) {
|
|
612
|
-
acc.value.push(result.value);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return acc;
|
|
616
|
-
};
|
|
617
|
-
var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
|
|
618
|
-
var Result;
|
|
619
|
-
(function(Result2) {
|
|
620
|
-
function fromThrowable(fn, errorFn) {
|
|
621
|
-
return (...args) => {
|
|
622
|
-
try {
|
|
623
|
-
const result = fn(...args);
|
|
624
|
-
return ok(result);
|
|
625
|
-
} catch (e) {
|
|
626
|
-
return err(errorFn ? errorFn(e) : e);
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
Result2.fromThrowable = fromThrowable;
|
|
631
|
-
function combine(resultList) {
|
|
632
|
-
return combineResultList(resultList);
|
|
633
|
-
}
|
|
634
|
-
Result2.combine = combine;
|
|
635
|
-
function combineWithAllErrors(resultList) {
|
|
636
|
-
return combineResultListWithAllErrors(resultList);
|
|
637
|
-
}
|
|
638
|
-
Result2.combineWithAllErrors = combineWithAllErrors;
|
|
639
|
-
})(Result || (Result = {}));
|
|
640
|
-
function ok(value) {
|
|
641
|
-
return new Ok(value);
|
|
642
|
-
}
|
|
643
|
-
function err(err2) {
|
|
644
|
-
return new Err(err2);
|
|
645
|
-
}
|
|
646
|
-
class Ok {
|
|
647
|
-
constructor(value) {
|
|
648
|
-
this.value = value;
|
|
649
|
-
}
|
|
650
|
-
isOk() {
|
|
651
|
-
return true;
|
|
652
|
-
}
|
|
653
|
-
isErr() {
|
|
654
|
-
return !this.isOk();
|
|
655
|
-
}
|
|
656
|
-
map(f) {
|
|
657
|
-
return ok(f(this.value));
|
|
658
|
-
}
|
|
659
|
-
mapErr(_f) {
|
|
660
|
-
return ok(this.value);
|
|
661
|
-
}
|
|
662
|
-
andThen(f) {
|
|
663
|
-
return f(this.value);
|
|
664
|
-
}
|
|
665
|
-
andThrough(f) {
|
|
666
|
-
return f(this.value).map((_value) => this.value);
|
|
667
|
-
}
|
|
668
|
-
andTee(f) {
|
|
669
|
-
try {
|
|
670
|
-
f(this.value);
|
|
671
|
-
} catch (e) {}
|
|
672
|
-
return ok(this.value);
|
|
673
|
-
}
|
|
674
|
-
orTee(_f) {
|
|
675
|
-
return ok(this.value);
|
|
676
|
-
}
|
|
677
|
-
orElse(_f) {
|
|
678
|
-
return ok(this.value);
|
|
679
|
-
}
|
|
680
|
-
asyncAndThen(f) {
|
|
681
|
-
return f(this.value);
|
|
682
|
-
}
|
|
683
|
-
asyncAndThrough(f) {
|
|
684
|
-
return f(this.value).map(() => this.value);
|
|
685
|
-
}
|
|
686
|
-
asyncMap(f) {
|
|
687
|
-
return ResultAsync.fromSafePromise(f(this.value));
|
|
688
|
-
}
|
|
689
|
-
unwrapOr(_v) {
|
|
690
|
-
return this.value;
|
|
691
|
-
}
|
|
692
|
-
match(ok2, _err) {
|
|
693
|
-
return ok2(this.value);
|
|
694
|
-
}
|
|
695
|
-
safeUnwrap() {
|
|
696
|
-
const value = this.value;
|
|
697
|
-
return function* () {
|
|
698
|
-
return value;
|
|
699
|
-
}();
|
|
700
|
-
}
|
|
701
|
-
_unsafeUnwrap(_) {
|
|
702
|
-
return this.value;
|
|
703
|
-
}
|
|
704
|
-
_unsafeUnwrapErr(config) {
|
|
705
|
-
throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
|
|
706
|
-
}
|
|
707
|
-
*[Symbol.iterator]() {
|
|
708
|
-
return this.value;
|
|
709
|
-
}
|
|
710
320
|
}
|
|
711
321
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
this.error = error;
|
|
715
|
-
}
|
|
716
|
-
isOk() {
|
|
717
|
-
return false;
|
|
718
|
-
}
|
|
719
|
-
isErr() {
|
|
720
|
-
return !this.isOk();
|
|
721
|
-
}
|
|
722
|
-
map(_f) {
|
|
723
|
-
return err(this.error);
|
|
724
|
-
}
|
|
725
|
-
mapErr(f) {
|
|
726
|
-
return err(f(this.error));
|
|
727
|
-
}
|
|
728
|
-
andThrough(_f) {
|
|
729
|
-
return err(this.error);
|
|
730
|
-
}
|
|
731
|
-
andTee(_f) {
|
|
732
|
-
return err(this.error);
|
|
733
|
-
}
|
|
734
|
-
orTee(f) {
|
|
735
|
-
try {
|
|
736
|
-
f(this.error);
|
|
737
|
-
} catch (e) {}
|
|
738
|
-
return err(this.error);
|
|
739
|
-
}
|
|
740
|
-
andThen(_f) {
|
|
741
|
-
return err(this.error);
|
|
742
|
-
}
|
|
743
|
-
orElse(f) {
|
|
744
|
-
return f(this.error);
|
|
745
|
-
}
|
|
746
|
-
asyncAndThen(_f) {
|
|
747
|
-
return errAsync(this.error);
|
|
748
|
-
}
|
|
749
|
-
asyncAndThrough(_f) {
|
|
750
|
-
return errAsync(this.error);
|
|
751
|
-
}
|
|
752
|
-
asyncMap(_f) {
|
|
753
|
-
return errAsync(this.error);
|
|
754
|
-
}
|
|
755
|
-
unwrapOr(v) {
|
|
756
|
-
return v;
|
|
757
|
-
}
|
|
758
|
-
match(_ok, err2) {
|
|
759
|
-
return err2(this.error);
|
|
760
|
-
}
|
|
761
|
-
safeUnwrap() {
|
|
762
|
-
const error = this.error;
|
|
763
|
-
return function* () {
|
|
764
|
-
yield err(error);
|
|
765
|
-
throw new Error("Do not use this generator out of `safeTry`");
|
|
766
|
-
}();
|
|
767
|
-
}
|
|
768
|
-
_unsafeUnwrap(config) {
|
|
769
|
-
throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
|
|
770
|
-
}
|
|
771
|
-
_unsafeUnwrapErr(_) {
|
|
772
|
-
return this.error;
|
|
773
|
-
}
|
|
774
|
-
*[Symbol.iterator]() {
|
|
775
|
-
const self = this;
|
|
776
|
-
yield self;
|
|
777
|
-
return self;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
var fromThrowable = Result.fromThrowable;
|
|
322
|
+
// src/VexConnection.ts
|
|
323
|
+
import { err, ok, ResultAsync } from "neverthrow";
|
|
781
324
|
|
|
782
325
|
// src/VexPacketBase.ts
|
|
783
326
|
var MATCH_STATUS_ALT_ACK = 167;
|
|
@@ -872,79 +415,115 @@ class CrcGenerator {
|
|
|
872
415
|
}
|
|
873
416
|
}
|
|
874
417
|
|
|
875
|
-
// src/
|
|
876
|
-
var
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
}
|
|
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
|
+
}
|
|
948
527
|
|
|
949
528
|
// src/VexFirmwareVersion.ts
|
|
950
529
|
class VexFirmwareVersion {
|
|
@@ -1015,7 +594,7 @@ class PacketView extends DataView {
|
|
|
1015
594
|
super(buffer, offset, length);
|
|
1016
595
|
}
|
|
1017
596
|
static fromPacket(packet) {
|
|
1018
|
-
const view = new PacketView(packet.data.buffer, packet.data.byteOffset);
|
|
597
|
+
const view = new PacketView(packet.data.buffer, packet.data.byteOffset, packet.data.byteLength);
|
|
1019
598
|
view.position = packet.ackIndex + 1;
|
|
1020
599
|
return view;
|
|
1021
600
|
}
|
|
@@ -1077,12 +656,12 @@ class PacketView extends DataView {
|
|
|
1077
656
|
}
|
|
1078
657
|
|
|
1079
658
|
// src/VexPacketModels.ts
|
|
1080
|
-
var
|
|
659
|
+
var textEncoder2 = new TextEncoder;
|
|
1081
660
|
function filePayload(a, b, fileName) {
|
|
1082
661
|
const payload = new Uint8Array(26);
|
|
1083
662
|
payload[0] = a;
|
|
1084
663
|
payload[1] = b;
|
|
1085
|
-
payload.set(encodeFixedText(fileName, "Filename",
|
|
664
|
+
payload.set(encodeFixedText(fileName, "Filename", 24), 2);
|
|
1086
665
|
return payload;
|
|
1087
666
|
}
|
|
1088
667
|
var clamp100 = (value) => value !== undefined && value > 100 ? 100 : value;
|
|
@@ -1150,7 +729,7 @@ class InitFileTransferH2DPacket extends DeviceBoundPacket {
|
|
|
1150
729
|
const timestamp = (Date.now() / 1000 >>> 0) - PacketEncoder.J2000_EPOCH;
|
|
1151
730
|
view.setUint32(20, timestamp, true);
|
|
1152
731
|
payload.set(version.toUint8Array(), 24);
|
|
1153
|
-
payload.set(encodeFixedText(name, "Filename",
|
|
732
|
+
payload.set(encodeFixedText(name, "Filename", 24), 28);
|
|
1154
733
|
super(payload);
|
|
1155
734
|
}
|
|
1156
735
|
}
|
|
@@ -1345,7 +924,7 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
|
|
|
1345
924
|
static COMMAND_EXTENDED_ID = 47;
|
|
1346
925
|
constructor(key, value) {
|
|
1347
926
|
const strk = encodeFixedText(key, "Key", 31);
|
|
1348
|
-
const strv =
|
|
927
|
+
const strv = textEncoder2.encode(value);
|
|
1349
928
|
if (strk.byteLength + strv.byteLength + 20 > 32767) {
|
|
1350
929
|
throw new RangeError("Key and value are too large for a protocol packet");
|
|
1351
930
|
}
|
|
@@ -1508,7 +1087,7 @@ class ReadFileReplyD2HPacket extends HostBoundPacket {
|
|
|
1508
1087
|
const view = PacketView.fromPacket(this);
|
|
1509
1088
|
this.addr = view.nextUint32();
|
|
1510
1089
|
this.length = this.payloadSize - 8;
|
|
1511
|
-
this.buf = this.data.
|
|
1090
|
+
this.buf = this.data.subarray(view.position, view.position + this.length);
|
|
1512
1091
|
}
|
|
1513
1092
|
}
|
|
1514
1093
|
|
|
@@ -1808,185 +1387,95 @@ class ReadKeyValueReplyD2HPacket extends HostBoundPacket {
|
|
|
1808
1387
|
value;
|
|
1809
1388
|
constructor(data) {
|
|
1810
1389
|
super(data);
|
|
1811
|
-
this.value = PacketView.fromPacket(this).nextVarNTBS(255);
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
class WriteKeyValueReplyD2HPacket extends HostBoundPacket {
|
|
1816
|
-
static COMMAND_ID = 86;
|
|
1817
|
-
static COMMAND_EXTENDED_ID = 47;
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
|
|
1821
|
-
static COMMAND_ID = 86;
|
|
1822
|
-
static COMMAND_EXTENDED_ID = 49;
|
|
1823
|
-
slotFlags;
|
|
1824
|
-
slots;
|
|
1825
|
-
constructor(data, start = 1) {
|
|
1826
|
-
super(data);
|
|
1827
|
-
const view = PacketView.fromPacket(this);
|
|
1828
|
-
this.slotFlags = view.nextUint8();
|
|
1829
|
-
this.slots = [];
|
|
1830
|
-
for (let i = 0;i < 4; i++) {
|
|
1831
|
-
if ((this.slotFlags & 1 << start - 1 + i) === 0)
|
|
1832
|
-
continue;
|
|
1833
|
-
const icon = view.nextUint16();
|
|
1834
|
-
const nameLen = view.nextUint8();
|
|
1835
|
-
this.slots.push({
|
|
1836
|
-
slot: start + i,
|
|
1837
|
-
icon,
|
|
1838
|
-
name: view.nextString(nameLen)
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
class GetSlot5to8InfoReplyD2HPacket extends GetSlot1to4InfoReplyD2HPacket {
|
|
1845
|
-
static COMMAND_ID = 86;
|
|
1846
|
-
static COMMAND_EXTENDED_ID = 50;
|
|
1847
|
-
slotStartIndex = 5;
|
|
1848
|
-
constructor(data) {
|
|
1849
|
-
super(data, 5);
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
class FactoryStatusReplyD2HPacket extends HostBoundPacket {
|
|
1854
|
-
static COMMAND_ID = 86;
|
|
1855
|
-
static COMMAND_EXTENDED_ID = 241;
|
|
1856
|
-
status;
|
|
1857
|
-
percent;
|
|
1858
|
-
constructor(data) {
|
|
1859
|
-
super(data);
|
|
1860
|
-
const view = PacketView.fromPacket(this);
|
|
1861
|
-
this.status = view.nextUint8();
|
|
1862
|
-
this.percent = view.nextUint8();
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
class FactoryEnableReplyD2HPacket extends HostBoundPacket {
|
|
1867
|
-
static COMMAND_ID = 86;
|
|
1868
|
-
static COMMAND_EXTENDED_ID = 255;
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
// src/VexPacketEncoder.ts
|
|
1872
|
-
var textEncoder2 = new TextEncoder;
|
|
1873
|
-
var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
|
|
1874
|
-
function encodeFixedText(value, field, maxBytes) {
|
|
1875
|
-
const encoded = textEncoder2.encode(value);
|
|
1876
|
-
if (encoded.byteLength > maxBytes) {
|
|
1877
|
-
throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
|
|
1878
|
-
}
|
|
1879
|
-
return encoded;
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
class PacketEncoder {
|
|
1883
|
-
static HEADERS_LENGTH = 4;
|
|
1884
|
-
static HEADER_TO_DEVICE = [201, 54, 184, 71];
|
|
1885
|
-
static HEADER_TO_HOST = [170, 85];
|
|
1886
|
-
static J2000_EPOCH = 946684800;
|
|
1887
|
-
vexVersion = 0;
|
|
1888
|
-
crcgen = new CrcGenerator;
|
|
1889
|
-
allPacketsTable = new Map;
|
|
1890
|
-
static getInstance() {
|
|
1891
|
-
Packet.ENCODER ??= new PacketEncoder;
|
|
1892
|
-
return Packet.ENCODER;
|
|
1893
|
-
}
|
|
1894
|
-
constructor() {
|
|
1895
|
-
for (const packet of Object.values(exports_VexPacketModels)) {
|
|
1896
|
-
if (typeof packet === "function" && packet.prototype instanceof HostBoundPacket) {
|
|
1897
|
-
const type = packet;
|
|
1898
|
-
let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
|
|
1899
|
-
if (byExtendedId === undefined) {
|
|
1900
|
-
byExtendedId = new Map;
|
|
1901
|
-
this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
|
|
1902
|
-
}
|
|
1903
|
-
byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
getPacketType(commandId, commandExtendedId) {
|
|
1908
|
-
if (commandId === undefined)
|
|
1909
|
-
return;
|
|
1910
|
-
return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
|
|
1911
|
-
}
|
|
1912
|
-
createHeader(buf) {
|
|
1913
|
-
if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
|
|
1914
|
-
buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
|
|
1915
|
-
}
|
|
1916
|
-
const h = new Uint8Array(buf);
|
|
1917
|
-
h.set(HEADER_TO_DEVICE);
|
|
1918
|
-
return h;
|
|
1919
|
-
}
|
|
1920
|
-
cdcCommand(cmd) {
|
|
1921
|
-
const h = new Uint8Array(5);
|
|
1922
|
-
h.set(HEADER_TO_DEVICE);
|
|
1923
|
-
h[4] = cmd;
|
|
1924
|
-
return h;
|
|
1925
|
-
}
|
|
1926
|
-
cdcCommandWithData(cmd, data) {
|
|
1927
|
-
const h = new Uint8Array(6 + data.length);
|
|
1928
|
-
h.set(HEADER_TO_DEVICE);
|
|
1929
|
-
h[4] = cmd;
|
|
1930
|
-
h[5] = data.length;
|
|
1931
|
-
h.set(data, 6);
|
|
1932
|
-
return h;
|
|
1933
|
-
}
|
|
1934
|
-
cdc2Command(cmd, ext) {
|
|
1935
|
-
const h = new Uint8Array(9);
|
|
1936
|
-
h.set(HEADER_TO_DEVICE);
|
|
1937
|
-
h[4] = cmd;
|
|
1938
|
-
h[5] = ext;
|
|
1939
|
-
h[6] = 0;
|
|
1940
|
-
this.appendCrc16(h);
|
|
1941
|
-
return h;
|
|
1942
|
-
}
|
|
1943
|
-
cdc2CommandBufferLength(data) {
|
|
1944
|
-
return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
|
|
1945
|
-
}
|
|
1946
|
-
cdc2CommandWithData(cmd, ext, data) {
|
|
1947
|
-
const h = new Uint8Array(this.cdc2CommandBufferLength(data));
|
|
1948
|
-
h.set(HEADER_TO_DEVICE);
|
|
1949
|
-
h[4] = cmd;
|
|
1950
|
-
h[5] = ext;
|
|
1951
|
-
if (data.length < 128) {
|
|
1952
|
-
h[6] = data.length;
|
|
1953
|
-
h.set(data, 7);
|
|
1954
|
-
} else {
|
|
1955
|
-
h[6] = data.length >>> 8 | 128;
|
|
1956
|
-
h[7] = data.length & 255;
|
|
1957
|
-
h.set(data, 8);
|
|
1958
|
-
}
|
|
1959
|
-
this.appendCrc16(h);
|
|
1960
|
-
return h;
|
|
1961
|
-
}
|
|
1962
|
-
appendCrc16(h) {
|
|
1963
|
-
const crc = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
|
|
1964
|
-
h[h.length - 2] = crc >>> 8;
|
|
1965
|
-
h[h.length - 1] = crc & 255;
|
|
1966
|
-
}
|
|
1967
|
-
validateHeader(data) {
|
|
1968
|
-
return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
|
|
1390
|
+
this.value = PacketView.fromPacket(this).nextVarNTBS(255);
|
|
1969
1391
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
class WriteKeyValueReplyD2HPacket extends HostBoundPacket {
|
|
1395
|
+
static COMMAND_ID = 86;
|
|
1396
|
+
static COMMAND_EXTENDED_ID = 47;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
|
|
1400
|
+
static COMMAND_ID = 86;
|
|
1401
|
+
static COMMAND_EXTENDED_ID = 49;
|
|
1402
|
+
slotFlags;
|
|
1403
|
+
slots;
|
|
1404
|
+
constructor(data, start = 1) {
|
|
1405
|
+
super(data);
|
|
1406
|
+
const view = PacketView.fromPacket(this);
|
|
1407
|
+
this.slotFlags = view.nextUint8();
|
|
1408
|
+
this.slots = [];
|
|
1409
|
+
for (let i = 0;i < 4; i++) {
|
|
1410
|
+
if ((this.slotFlags & 1 << start - 1 + i) === 0)
|
|
1411
|
+
continue;
|
|
1412
|
+
const icon = view.nextUint16();
|
|
1413
|
+
const nameLen = view.nextUint8();
|
|
1414
|
+
this.slots.push({
|
|
1415
|
+
slot: start + i,
|
|
1416
|
+
icon,
|
|
1417
|
+
name: view.nextString(nameLen)
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1973
1420
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
class GetSlot5to8InfoReplyD2HPacket extends GetSlot1to4InfoReplyD2HPacket {
|
|
1424
|
+
static COMMAND_ID = 86;
|
|
1425
|
+
static COMMAND_EXTENDED_ID = 50;
|
|
1426
|
+
slotStartIndex = 5;
|
|
1427
|
+
constructor(data) {
|
|
1428
|
+
super(data, 5);
|
|
1977
1429
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
class FactoryStatusReplyD2HPacket extends HostBoundPacket {
|
|
1433
|
+
static COMMAND_ID = 86;
|
|
1434
|
+
static COMMAND_EXTENDED_ID = 241;
|
|
1435
|
+
status;
|
|
1436
|
+
percent;
|
|
1437
|
+
constructor(data) {
|
|
1438
|
+
super(data);
|
|
1439
|
+
const view = PacketView.fromPacket(this);
|
|
1440
|
+
this.status = view.nextUint8();
|
|
1441
|
+
this.percent = view.nextUint8();
|
|
1980
1442
|
}
|
|
1981
1443
|
}
|
|
1982
1444
|
|
|
1983
|
-
|
|
1984
|
-
|
|
1445
|
+
class FactoryEnableReplyD2HPacket extends HostBoundPacket {
|
|
1446
|
+
static COMMAND_ID = 86;
|
|
1447
|
+
static COMMAND_EXTENDED_ID = 255;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// src/VexScreenCapture.ts
|
|
1985
1451
|
var SCREEN_CAPTURE_HEIGHT = 272;
|
|
1986
1452
|
var SCREEN_CAPTURE_WIDTH = 480;
|
|
1987
1453
|
var SCREEN_CAPTURE_CHANNELS = 3;
|
|
1988
1454
|
var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
|
|
1989
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}`);
|
|
1460
|
+
}
|
|
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;
|
|
1471
|
+
}
|
|
1472
|
+
source += (SCREEN_CAPTURE_MESSAGE_WIDTH - SCREEN_CAPTURE_WIDTH) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
1473
|
+
}
|
|
1474
|
+
return pixels;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/VexConnection.ts
|
|
1478
|
+
var thePacketEncoder = PacketEncoder.getInstance();
|
|
1990
1479
|
|
|
1991
1480
|
class VexSerialConnection extends VexEventTarget {
|
|
1992
1481
|
filters = [{ usbVendorId: 10376 }];
|
|
@@ -1994,12 +1483,31 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
1994
1483
|
reader;
|
|
1995
1484
|
port;
|
|
1996
1485
|
serial;
|
|
1997
|
-
|
|
1486
|
+
pendingCallbacks = new Map;
|
|
1487
|
+
rawCallbacks = {
|
|
1488
|
+
head: undefined,
|
|
1489
|
+
tail: undefined
|
|
1490
|
+
};
|
|
1491
|
+
pendingCommandTails = new Map;
|
|
1998
1492
|
_onPortDisconnect = null;
|
|
1999
1493
|
_closePromise = null;
|
|
1494
|
+
_openPromise = null;
|
|
1495
|
+
_isClosing = false;
|
|
2000
1496
|
_wasConnected = false;
|
|
2001
1497
|
fileTransferTail = Promise.resolve();
|
|
2002
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
|
+
}
|
|
2003
1511
|
get isConnected() {
|
|
2004
1512
|
return this.port !== undefined && this.reader !== undefined && this.writer !== undefined;
|
|
2005
1513
|
}
|
|
@@ -2011,27 +1519,45 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2011
1519
|
this.serial = serial;
|
|
2012
1520
|
}
|
|
2013
1521
|
reportWarning(message, details) {
|
|
2014
|
-
this.
|
|
1522
|
+
this.emitSafely("warning", {
|
|
1523
|
+
message,
|
|
1524
|
+
details
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
emitSafely(eventName, data) {
|
|
1528
|
+
try {
|
|
1529
|
+
this.emit(eventName, data);
|
|
1530
|
+
} catch {}
|
|
2015
1531
|
}
|
|
2016
1532
|
async close() {
|
|
2017
1533
|
if (this._closePromise)
|
|
2018
1534
|
return this._closePromise;
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
this._closePromise =
|
|
1535
|
+
this._isClosing = true;
|
|
1536
|
+
const closing = this._closeAfterOpen();
|
|
1537
|
+
this._closePromise = closing;
|
|
2022
1538
|
try {
|
|
2023
|
-
await
|
|
1539
|
+
await closing;
|
|
2024
1540
|
} finally {
|
|
2025
|
-
this._closePromise
|
|
1541
|
+
if (this._closePromise === closing)
|
|
1542
|
+
this._closePromise = null;
|
|
1543
|
+
this._isClosing = false;
|
|
2026
1544
|
}
|
|
2027
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
|
+
}
|
|
2028
1554
|
_hasOpenResources() {
|
|
2029
|
-
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();
|
|
2030
1556
|
}
|
|
2031
1557
|
async _doClose() {
|
|
2032
|
-
for (const callback of this.
|
|
1558
|
+
for (const callback of this.drainPendingCallbacks()) {
|
|
2033
1559
|
clearTimeout(callback.timeout);
|
|
2034
|
-
callback.callback(
|
|
1560
|
+
callback.callback(258 /* NOT_CONNECTED */);
|
|
2035
1561
|
}
|
|
2036
1562
|
const onDisconnect = this._onPortDisconnect;
|
|
2037
1563
|
this._onPortDisconnect = null;
|
|
@@ -2080,11 +1606,22 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2080
1606
|
}
|
|
2081
1607
|
if (this._wasConnected) {
|
|
2082
1608
|
this._wasConnected = false;
|
|
2083
|
-
this.
|
|
1609
|
+
this.emitSafely("disconnected", undefined);
|
|
2084
1610
|
}
|
|
2085
1611
|
}
|
|
2086
1612
|
open(use = 0, askUser = true) {
|
|
2087
|
-
|
|
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);
|
|
2088
1625
|
}
|
|
2089
1626
|
async _open(use, askUser) {
|
|
2090
1627
|
if (this._closePromise !== null)
|
|
@@ -2117,38 +1654,63 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2117
1654
|
this.reader = this.port.readable.getReader();
|
|
2118
1655
|
this.startReader();
|
|
2119
1656
|
this._wasConnected = true;
|
|
2120
|
-
this.
|
|
1657
|
+
this.emitSafely("connected", undefined);
|
|
2121
1658
|
return ok("opened");
|
|
2122
1659
|
} catch (e) {
|
|
2123
|
-
await this.
|
|
1660
|
+
await this._doClose();
|
|
2124
1661
|
return err(toVexSerialError(e, "io"));
|
|
2125
1662
|
}
|
|
2126
1663
|
}
|
|
2127
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) {
|
|
2128
1689
|
return new Promise((resolve) => {
|
|
2129
|
-
if (this.writer === undefined) {
|
|
2130
|
-
resolve(
|
|
1690
|
+
if (this.writer === undefined || this._isClosing) {
|
|
1691
|
+
resolve(258 /* NOT_CONNECTED */);
|
|
2131
1692
|
return;
|
|
2132
1693
|
}
|
|
2133
1694
|
const data = rawData instanceof DeviceBoundPacket ? rawData.data : rawData;
|
|
1695
|
+
const queue = rawData instanceof DeviceBoundPacket ? this.getPendingQueue(rawData.commandId, rawData.commandExtendedId, true) : this.rawCallbacks;
|
|
2134
1696
|
const cb = {
|
|
1697
|
+
active: true,
|
|
2135
1698
|
callback: resolve,
|
|
2136
1699
|
timeout: setTimeout(() => {
|
|
2137
|
-
|
|
2138
|
-
if (index === -1)
|
|
1700
|
+
if (!this.removePendingCallback(cb))
|
|
2139
1701
|
return;
|
|
2140
|
-
this.callbacksQueue.splice(index, 1);
|
|
2141
1702
|
cb.callback(256 /* TIMEOUT */);
|
|
2142
1703
|
}, timeout),
|
|
1704
|
+
next: undefined,
|
|
1705
|
+
previous: undefined,
|
|
1706
|
+
queue,
|
|
2143
1707
|
wantedCommandId: rawData instanceof DeviceBoundPacket ? rawData.commandId : undefined,
|
|
2144
1708
|
wantedCommandExId: rawData instanceof DeviceBoundPacket ? rawData.commandExtendedId : undefined
|
|
2145
1709
|
};
|
|
2146
|
-
this.
|
|
1710
|
+
this.enqueuePendingCallback(cb);
|
|
2147
1711
|
this.writer.write(data).catch(() => {
|
|
2148
|
-
|
|
2149
|
-
if (index === -1)
|
|
1712
|
+
if (!this.removePendingCallback(cb))
|
|
2150
1713
|
return;
|
|
2151
|
-
this.callbacksQueue.splice(index, 1);
|
|
2152
1714
|
clearTimeout(cb.timeout);
|
|
2153
1715
|
resolve(257 /* WRITE_ERROR */);
|
|
2154
1716
|
});
|
|
@@ -2159,6 +1721,9 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2159
1721
|
const result = await this.writeDataAsync(packet, timeout);
|
|
2160
1722
|
if (result instanceof ReplyType)
|
|
2161
1723
|
return ok(result);
|
|
1724
|
+
if (result === 258 /* NOT_CONNECTED */) {
|
|
1725
|
+
return err(new VexNotConnectedError);
|
|
1726
|
+
}
|
|
2162
1727
|
return err(new VexProtocolError(expectedReplyMessage(packet, ReplyType, result), typeof result === "number" ? result : undefined));
|
|
2163
1728
|
})());
|
|
2164
1729
|
}
|
|
@@ -2169,56 +1734,48 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2169
1734
|
const { value: readData, done: isDone } = await this.reader.read();
|
|
2170
1735
|
if (isDone)
|
|
2171
1736
|
throw new Error("No data");
|
|
2172
|
-
cache
|
|
1737
|
+
cache.append(readData);
|
|
2173
1738
|
}
|
|
2174
|
-
return cache;
|
|
2175
1739
|
}
|
|
2176
1740
|
async startReader() {
|
|
2177
|
-
|
|
1741
|
+
const cache = new ReceiveBuffer;
|
|
2178
1742
|
let sliceIdx = 0;
|
|
2179
1743
|
for (;; )
|
|
2180
1744
|
try {
|
|
2181
|
-
|
|
1745
|
+
await this.readData(cache, 5);
|
|
2182
1746
|
sliceIdx = 0;
|
|
2183
|
-
while (!thePacketEncoder.validateHeader(cache)) {
|
|
2184
|
-
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]);
|
|
2185
1750
|
if (nextHeader >= 0) {
|
|
2186
|
-
cache
|
|
1751
|
+
cache.discard(nextHeader);
|
|
2187
1752
|
} else {
|
|
2188
|
-
cache
|
|
1753
|
+
cache.discard(bytes.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : bytes.length);
|
|
2189
1754
|
}
|
|
2190
|
-
|
|
1755
|
+
await this.readData(cache, 5);
|
|
2191
1756
|
}
|
|
2192
|
-
const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache);
|
|
2193
|
-
const n = thePacketEncoder.getHostHeaderLength(cache);
|
|
1757
|
+
const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache.bytes);
|
|
1758
|
+
const n = thePacketEncoder.getHostHeaderLength(cache.bytes);
|
|
2194
1759
|
const totalSize = n + payloadExpectedSize;
|
|
2195
|
-
|
|
1760
|
+
await this.readData(cache, totalSize);
|
|
2196
1761
|
sliceIdx = totalSize;
|
|
2197
|
-
const
|
|
1762
|
+
const packet = cache.copy(totalSize);
|
|
1763
|
+
const cmdId = packet[2];
|
|
2198
1764
|
const hasExtId = cmdId === 88 || cmdId === 86;
|
|
2199
|
-
const cmdExId = hasExtId ?
|
|
2200
|
-
const ack =
|
|
1765
|
+
const cmdExId = hasExtId ? packet[n] : undefined;
|
|
1766
|
+
const ack = packet[n + 1];
|
|
2201
1767
|
if (hasExtId) {
|
|
2202
|
-
if (!thePacketEncoder.validateMessageCdc(
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
const candidate = this.callbacksQueue[i];
|
|
2209
|
-
if (candidate.wantedCommandId === undefined && candidate.wantedCommandExId === undefined) {
|
|
2210
|
-
if (untypedIdx === -1)
|
|
2211
|
-
untypedIdx = i;
|
|
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
|
+
});
|
|
2212
1774
|
continue;
|
|
2213
1775
|
}
|
|
2214
|
-
if ((candidate.wantedCommandId === undefined || candidate.wantedCommandId === cmdId) && (candidate.wantedCommandExId === undefined || candidate.wantedCommandExId === cmdExId)) {
|
|
2215
|
-
matchIdx = i;
|
|
2216
|
-
break;
|
|
2217
|
-
}
|
|
2218
1776
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
if (matchIdx === -1) {
|
|
1777
|
+
const callbackInfo = this.shiftPendingCallback(cmdId, cmdExId) ?? this.shiftRawCallback();
|
|
1778
|
+
if (callbackInfo === undefined) {
|
|
2222
1779
|
this.reportWarning("received a reply with no matching request", {
|
|
2223
1780
|
commandId: cmdId,
|
|
2224
1781
|
commandExtendedId: cmdExId,
|
|
@@ -2226,38 +1783,117 @@ class VexSerialConnection extends VexEventTarget {
|
|
|
2226
1783
|
});
|
|
2227
1784
|
continue;
|
|
2228
1785
|
}
|
|
2229
|
-
const callbackInfo = this.callbacksQueue[matchIdx];
|
|
2230
1786
|
const wantedCmdId = callbackInfo.wantedCommandId;
|
|
2231
1787
|
const wantedCmdExId = callbackInfo.wantedCommandExId;
|
|
2232
|
-
const data = cache.slice(0, sliceIdx);
|
|
2233
1788
|
const PackageType = thePacketEncoder.getPacketType(wantedCmdId, wantedCmdExId);
|
|
2234
1789
|
if (wantedCmdId === undefined || PackageType === undefined) {
|
|
2235
1790
|
if (wantedCmdId !== undefined) {
|
|
2236
1791
|
this.reportWarning("no packet class is registered for the wanted command", { commandId: wantedCmdId, commandExtendedId: wantedCmdExId });
|
|
2237
1792
|
}
|
|
2238
|
-
callbackInfo.callback(
|
|
1793
|
+
callbackInfo.callback(packet.buffer);
|
|
2239
1794
|
} else {
|
|
2240
|
-
if (
|
|
2241
|
-
callbackInfo.callback(new PackageType(
|
|
1795
|
+
if (PackageType.isValidPacket(packet, n)) {
|
|
1796
|
+
callbackInfo.callback(new PackageType(packet));
|
|
2242
1797
|
} else {
|
|
2243
1798
|
this.reportWarning("reply failed packet validation; delivering its ack instead", { commandId: cmdId, commandExtendedId: cmdExId, ack });
|
|
2244
1799
|
callbackInfo.callback(ack);
|
|
2245
1800
|
}
|
|
2246
1801
|
}
|
|
2247
1802
|
clearTimeout(callbackInfo.timeout);
|
|
2248
|
-
this.callbacksQueue.splice(matchIdx, 1);
|
|
2249
1803
|
} catch (e) {
|
|
2250
1804
|
if (!(e instanceof Error && e.message === "No data")) {
|
|
2251
1805
|
this.reportWarning("reader loop stopped by a read error", {
|
|
2252
1806
|
error: e,
|
|
2253
|
-
pendingBytes: cache
|
|
1807
|
+
pendingBytes: cache.bytes
|
|
2254
1808
|
});
|
|
2255
1809
|
}
|
|
2256
1810
|
await this.close();
|
|
2257
1811
|
break;
|
|
2258
1812
|
} finally {
|
|
2259
|
-
cache
|
|
1813
|
+
cache.discard(sliceIdx);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
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;
|
|
2260
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;
|
|
2261
1897
|
}
|
|
2262
1898
|
query1() {
|
|
2263
1899
|
return this.request(new Query1H2DPacket, Query1ReplyD2HPacket, 100);
|
|
@@ -2375,7 +2011,9 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2375
2011
|
let bufferOffset = 0;
|
|
2376
2012
|
const fileBuf = new Uint8Array(fileSize);
|
|
2377
2013
|
while (bufferOffset < fileSize) {
|
|
2378
|
-
const
|
|
2014
|
+
const remainingSize = fileSize - bufferOffset;
|
|
2015
|
+
const chunkSize = Math.min(bufferChunkSize, remainingSize);
|
|
2016
|
+
const requestedSize = chunkSize + 3 & ~3;
|
|
2379
2017
|
const p2Result = await this.request(new ReadFileH2DPacket(nextAddress, requestedSize), ReadFileReplyD2HPacket, 3000);
|
|
2380
2018
|
if (p2Result.isErr())
|
|
2381
2019
|
throw p2Result.error;
|
|
@@ -2386,9 +2024,10 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2386
2024
|
if (p2.length <= 0 || p2.length > requestedSize || p2.buf.byteLength !== p2.length) {
|
|
2387
2025
|
throw new VexTransferError(`ReadFileReplyD2HPacket returned invalid length ${p2.length}`);
|
|
2388
2026
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2027
|
+
const receivedSize = Math.min(p2.length, remainingSize);
|
|
2028
|
+
fileBuf.set(p2.buf.subarray(0, receivedSize), bufferOffset);
|
|
2029
|
+
bufferOffset += receivedSize;
|
|
2030
|
+
nextAddress += receivedSize;
|
|
2392
2031
|
progressCallback?.(bufferOffset, fileSize);
|
|
2393
2032
|
}
|
|
2394
2033
|
transferFailed = false;
|
|
@@ -2543,7 +2182,7 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2543
2182
|
filename: "screen",
|
|
2544
2183
|
vendor: 15 /* SYS */,
|
|
2545
2184
|
loadAddress: 0,
|
|
2546
|
-
size:
|
|
2185
|
+
size: SCREEN_CAPTURE_FRAMEBUFFER_SIZE
|
|
2547
2186
|
}, 2 /* FILE_TARGET_CBUF */, progressCallback);
|
|
2548
2187
|
if (framebuffer.isErr())
|
|
2549
2188
|
return err(framebuffer.error);
|
|
@@ -2568,34 +2207,60 @@ class V5SerialConnection extends VexSerialConnection {
|
|
|
2568
2207
|
return this.request(new SelectDashH2DPacket(screen, port), SelectDashReplyD2HPacket);
|
|
2569
2208
|
}
|
|
2570
2209
|
}
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
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;
|
|
2259
|
+
}
|
|
2582
2260
|
}
|
|
2583
2261
|
function getTransferChunkSize(windowSize) {
|
|
2584
2262
|
return windowSize > 0 && windowSize <= USER_PROG_CHUNK_SIZE ? windowSize : USER_PROG_CHUNK_SIZE;
|
|
2585
2263
|
}
|
|
2586
|
-
function convertScreenCapture(framebuffer) {
|
|
2587
|
-
const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
|
|
2588
|
-
for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
|
|
2589
|
-
for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
|
|
2590
|
-
const source = (row * SCREEN_CAPTURE_MESSAGE_WIDTH + column) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
|
|
2591
|
-
const target = (row * SCREEN_CAPTURE_WIDTH + column) * SCREEN_CAPTURE_CHANNELS;
|
|
2592
|
-
pixels[target] = framebuffer[source + 2] ?? 0;
|
|
2593
|
-
pixels[target + 1] = framebuffer[source + 1] ?? 0;
|
|
2594
|
-
pixels[target + 2] = framebuffer[source] ?? 0;
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
return pixels;
|
|
2598
|
-
}
|
|
2599
2264
|
function wrapTransfer(conn, operation) {
|
|
2600
2265
|
return new ResultAsync(conn.withFileTransfer(async () => {
|
|
2601
2266
|
try {
|
|
@@ -2618,7 +2283,11 @@ function expectedReplyMessage(packet, ReplyType, reply) {
|
|
|
2618
2283
|
function ackTypeName(ackType) {
|
|
2619
2284
|
return `AckType.${AckType[ackType] ?? "UNKNOWN"} (${ackType})`;
|
|
2620
2285
|
}
|
|
2286
|
+
// src/VexDeviceState.ts
|
|
2287
|
+
import { err as err4, ok as ok4, ResultAsync as ResultAsync4 } from "neverthrow";
|
|
2288
|
+
|
|
2621
2289
|
// src/VexFirmware.ts
|
|
2290
|
+
import { err as err2, errAsync, ok as ok2, ResultAsync as ResultAsync2 } from "neverthrow";
|
|
2622
2291
|
var MAX_CATALOG_BYTES = 4 * 1024;
|
|
2623
2292
|
var MAX_VEXOS_BYTES = 64 * 1024 * 1024;
|
|
2624
2293
|
var MAX_FIRMWARE_IMAGE_BYTES = 32 * 1024 * 1024;
|
|
@@ -2631,7 +2300,7 @@ function downloadFileFromInternet(link, options = {}) {
|
|
|
2631
2300
|
if (timeout < 0) {
|
|
2632
2301
|
return errAsync(new VexInvalidArgumentError("timeout must be non-negative"));
|
|
2633
2302
|
}
|
|
2634
|
-
return new
|
|
2303
|
+
return new ResultAsync2(runDownload(link, maxBytes, timeout));
|
|
2635
2304
|
}
|
|
2636
2305
|
async function runDownload(link, maxBytes, timeout) {
|
|
2637
2306
|
const controller = new AbortController;
|
|
@@ -2641,20 +2310,20 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
2641
2310
|
try {
|
|
2642
2311
|
response = await fetch(link, { signal: controller.signal });
|
|
2643
2312
|
} catch (e) {
|
|
2644
|
-
return
|
|
2313
|
+
return err2(new VexDownloadError(`failed to download ${link} (${e instanceof Error ? e.message : String(e)})`));
|
|
2645
2314
|
}
|
|
2646
2315
|
if (!response.ok) {
|
|
2647
|
-
return
|
|
2316
|
+
return err2(new VexDownloadError(`failed to download ${link} (${response.status})`));
|
|
2648
2317
|
}
|
|
2649
2318
|
const declaredLength = response.headers.get("content-length");
|
|
2650
2319
|
if (declaredLength !== null) {
|
|
2651
2320
|
const declared = Number.parseInt(declaredLength, 10);
|
|
2652
2321
|
if (!Number.isNaN(declared) && declared > 0 && declared > maxBytes) {
|
|
2653
|
-
return
|
|
2322
|
+
return err2(new VexDownloadError(`declared content length ${declared} exceeds limit ${maxBytes} for ${link}`));
|
|
2654
2323
|
}
|
|
2655
2324
|
}
|
|
2656
2325
|
if (response.body == null) {
|
|
2657
|
-
return
|
|
2326
|
+
return err2(new VexDownloadError(`no response body for ${link}`));
|
|
2658
2327
|
}
|
|
2659
2328
|
const reader = response.body.getReader();
|
|
2660
2329
|
const chunks = [];
|
|
@@ -2671,7 +2340,7 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
2671
2340
|
try {
|
|
2672
2341
|
await reader.cancel();
|
|
2673
2342
|
} catch {}
|
|
2674
|
-
return
|
|
2343
|
+
return err2(new VexDownloadError(`downloaded body exceeds limit ${maxBytes} for ${link}`));
|
|
2675
2344
|
}
|
|
2676
2345
|
chunks.push(value);
|
|
2677
2346
|
}
|
|
@@ -2686,7 +2355,7 @@ async function runDownload(link, maxBytes, timeout) {
|
|
|
2686
2355
|
result.set(chunk, offset);
|
|
2687
2356
|
offset += chunk.byteLength;
|
|
2688
2357
|
}
|
|
2689
|
-
return
|
|
2358
|
+
return ok2(result.buffer);
|
|
2690
2359
|
} finally {
|
|
2691
2360
|
clearTimeout(timer);
|
|
2692
2361
|
}
|
|
@@ -2698,23 +2367,23 @@ function sleepUntilAsync(f, timeout, interval = 20) {
|
|
|
2698
2367
|
if (interval <= 0) {
|
|
2699
2368
|
return errAsync(new VexInvalidArgumentError("interval must be positive"));
|
|
2700
2369
|
}
|
|
2701
|
-
return new
|
|
2370
|
+
return new ResultAsync2(runSleepUntilAsync(f, timeout, interval));
|
|
2702
2371
|
}
|
|
2703
2372
|
async function runSleepUntilAsync(f, timeout, interval) {
|
|
2704
2373
|
const deadline = Date.now() + timeout;
|
|
2705
2374
|
while (Date.now() <= deadline) {
|
|
2706
2375
|
try {
|
|
2707
2376
|
if (await f())
|
|
2708
|
-
return
|
|
2377
|
+
return ok2(true);
|
|
2709
2378
|
} catch (e) {
|
|
2710
|
-
return
|
|
2379
|
+
return err2(toVexSerialError(e, "io"));
|
|
2711
2380
|
}
|
|
2712
2381
|
const remaining = deadline - Date.now();
|
|
2713
2382
|
if (remaining <= 0)
|
|
2714
2383
|
break;
|
|
2715
2384
|
await sleepInner(Math.min(interval, remaining));
|
|
2716
2385
|
}
|
|
2717
|
-
return
|
|
2386
|
+
return ok2(false);
|
|
2718
2387
|
}
|
|
2719
2388
|
function sleepUntil(f, timeout, interval = 20) {
|
|
2720
2389
|
if (timeout < 0) {
|
|
@@ -2723,29 +2392,29 @@ function sleepUntil(f, timeout, interval = 20) {
|
|
|
2723
2392
|
if (interval <= 0) {
|
|
2724
2393
|
return errAsync(new VexInvalidArgumentError("interval must be positive"));
|
|
2725
2394
|
}
|
|
2726
|
-
return new
|
|
2395
|
+
return new ResultAsync2(runSleepUntil(f, timeout, interval));
|
|
2727
2396
|
}
|
|
2728
2397
|
async function runSleepUntil(f, timeout, interval) {
|
|
2729
2398
|
const deadline = Date.now() + timeout;
|
|
2730
2399
|
while (Date.now() <= deadline) {
|
|
2731
2400
|
try {
|
|
2732
2401
|
if (f())
|
|
2733
|
-
return
|
|
2402
|
+
return ok2(true);
|
|
2734
2403
|
} catch (e) {
|
|
2735
|
-
return
|
|
2404
|
+
return err2(toVexSerialError(e, "io"));
|
|
2736
2405
|
}
|
|
2737
2406
|
const remaining = deadline - Date.now();
|
|
2738
2407
|
if (remaining <= 0)
|
|
2739
2408
|
break;
|
|
2740
2409
|
await sleepInner(Math.min(interval, remaining));
|
|
2741
2410
|
}
|
|
2742
|
-
return
|
|
2411
|
+
return ok2(false);
|
|
2743
2412
|
}
|
|
2744
2413
|
function sleep(ms) {
|
|
2745
2414
|
if (ms < 0) {
|
|
2746
2415
|
return errAsync(new VexInvalidArgumentError("ms must be non-negative"));
|
|
2747
2416
|
}
|
|
2748
|
-
return
|
|
2417
|
+
return ResultAsync2.fromSafePromise(sleepInner(ms));
|
|
2749
2418
|
}
|
|
2750
2419
|
async function sleepInner(ms) {
|
|
2751
2420
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -2755,7 +2424,7 @@ async function flashFactoryImage(conn, options, pcb) {
|
|
|
2755
2424
|
pcb(`FACTORY ENB ${label}`, 0, 0);
|
|
2756
2425
|
const enableReply = await conn.request(new FactoryEnableH2DPacket, FactoryEnableReplyD2HPacket);
|
|
2757
2426
|
if (enableReply.isErr())
|
|
2758
|
-
return
|
|
2427
|
+
return err2(enableReply.error);
|
|
2759
2428
|
const writeRequest = {
|
|
2760
2429
|
filename: "null.bin",
|
|
2761
2430
|
vendor: 1 /* USER */,
|
|
@@ -2766,26 +2435,26 @@ async function flashFactoryImage(conn, options, pcb) {
|
|
|
2766
2435
|
autoRun: true,
|
|
2767
2436
|
linkedFile: undefined
|
|
2768
2437
|
};
|
|
2769
|
-
const upload = await conn.
|
|
2438
|
+
const upload = await conn.uploadFileToDeviceUnlocked(writeRequest, (c, t) => {
|
|
2770
2439
|
pcb(`UPLOAD ${label}`, c, t);
|
|
2771
2440
|
});
|
|
2772
2441
|
if (upload.isErr())
|
|
2773
|
-
return
|
|
2442
|
+
return err2(upload.error);
|
|
2774
2443
|
if (!upload.value) {
|
|
2775
|
-
return
|
|
2444
|
+
return err2(new VexFirmwareError(`${label} upload was rejected by device`));
|
|
2776
2445
|
}
|
|
2777
2446
|
const deadline = Date.now() + 120000;
|
|
2778
2447
|
while (Date.now() < deadline) {
|
|
2779
2448
|
const statusReply = await conn.request(new FactoryStatusH2DPacket, FactoryStatusReplyD2HPacket, 1e4);
|
|
2780
2449
|
if (statusReply.isErr())
|
|
2781
|
-
return
|
|
2450
|
+
return err2(statusReply.error);
|
|
2782
2451
|
reportFactoryStatus(label, statusReply.value, pcb);
|
|
2783
2452
|
if (statusReply.value.status === 0 && statusReply.value.percent === 100) {
|
|
2784
|
-
return
|
|
2453
|
+
return ok2(undefined);
|
|
2785
2454
|
}
|
|
2786
2455
|
await sleepInner(500);
|
|
2787
2456
|
}
|
|
2788
|
-
return
|
|
2457
|
+
return err2(new VexFirmwareError(`${label} factory status timed out`));
|
|
2789
2458
|
}
|
|
2790
2459
|
function reportFactoryStatus(label, reply, pcb) {
|
|
2791
2460
|
switch (reply.status) {
|
|
@@ -2850,13 +2519,13 @@ async function extractFirmwareImages(usingVersion, vexos) {
|
|
|
2850
2519
|
return ordered;
|
|
2851
2520
|
}
|
|
2852
2521
|
function uploadFirmware(state, publicUrl = "https://content.vexrobotics.com/vexos/public/V5/", usingVersion, progressCallback) {
|
|
2853
|
-
return new
|
|
2522
|
+
return new ResultAsync2(runUploadFirmware(state, publicUrl, usingVersion, progressCallback));
|
|
2854
2523
|
}
|
|
2855
2524
|
async function runUploadFirmware(state, publicUrl, usingVersion, progressCallback) {
|
|
2856
2525
|
const device = state._instance;
|
|
2857
2526
|
const conn = device.connection;
|
|
2858
2527
|
if (conn == null || !conn.isConnected) {
|
|
2859
|
-
return
|
|
2528
|
+
return err2(new VexNotConnectedError);
|
|
2860
2529
|
}
|
|
2861
2530
|
const pcb = progressCallback ?? (() => {});
|
|
2862
2531
|
let version = usingVersion;
|
|
@@ -2866,20 +2535,20 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
|
|
|
2866
2535
|
maxBytes: MAX_CATALOG_BYTES
|
|
2867
2536
|
});
|
|
2868
2537
|
if (catalog.isErr())
|
|
2869
|
-
return
|
|
2538
|
+
return err2(catalog.error);
|
|
2870
2539
|
version = new TextDecoder().decode(catalog.value).trim();
|
|
2871
2540
|
pcb("FETCH CATALOG", 1, 1);
|
|
2872
2541
|
}
|
|
2873
2542
|
if (!/^[A-Za-z0-9._-]+$/.test(version)) {
|
|
2874
|
-
return
|
|
2543
|
+
return err2(new VexFirmwareError(`invalid VEXos version: ${version}`));
|
|
2875
2544
|
}
|
|
2876
2545
|
pcb("FETCH VEXOS", 0, 1);
|
|
2877
2546
|
const vexosResult = await downloadFileFromInternet(publicUrl + version + ".vexos", { maxBytes: MAX_VEXOS_BYTES });
|
|
2878
2547
|
if (vexosResult.isErr())
|
|
2879
|
-
return
|
|
2548
|
+
return err2(vexosResult.error);
|
|
2880
2549
|
const vexos = vexosResult.value;
|
|
2881
2550
|
if (vexos.byteLength === 0) {
|
|
2882
|
-
return
|
|
2551
|
+
return err2(new VexFirmwareError("VEXos archive is empty"));
|
|
2883
2552
|
}
|
|
2884
2553
|
pcb("FETCH VEXOS", 1, 1);
|
|
2885
2554
|
pcb("UNZIP VEXOS", 0, 1);
|
|
@@ -2888,52 +2557,59 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
|
|
|
2888
2557
|
images = await extractFirmwareImages(version, vexos);
|
|
2889
2558
|
} catch (e) {
|
|
2890
2559
|
if (e instanceof VexSerialError)
|
|
2891
|
-
return
|
|
2892
|
-
return
|
|
2560
|
+
return err2(e);
|
|
2561
|
+
return err2(toVexSerialError(e, "firmware"));
|
|
2893
2562
|
}
|
|
2894
2563
|
pcb("UNZIP VEXOS", 1, 1);
|
|
2895
2564
|
return state.withRefreshPaused(async () => {
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
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"));
|
|
2570
|
+
}
|
|
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"));
|
|
2574
|
+
}
|
|
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"));
|
|
2593
|
+
}
|
|
2919
2594
|
});
|
|
2920
2595
|
}
|
|
2921
2596
|
|
|
2922
2597
|
// src/VexTransfers.ts
|
|
2598
|
+
import { err as err3, ok as ok3, ResultAsync as ResultAsync3 } from "neverthrow";
|
|
2923
2599
|
function getValue(state, key) {
|
|
2924
|
-
return new
|
|
2600
|
+
return new ResultAsync3((async () => {
|
|
2925
2601
|
const conn = state._instance.connection;
|
|
2926
2602
|
if (conn == null || !conn.isConnected) {
|
|
2927
|
-
return
|
|
2603
|
+
return err3(new VexNotConnectedError);
|
|
2928
2604
|
}
|
|
2929
2605
|
return conn.request(new ReadKeyValueH2DPacket(key), ReadKeyValueReplyD2HPacket).map((result) => result.value);
|
|
2930
2606
|
})());
|
|
2931
2607
|
}
|
|
2932
2608
|
function setValue(state, key, value) {
|
|
2933
|
-
return new
|
|
2609
|
+
return new ResultAsync3((async () => {
|
|
2934
2610
|
const conn = state._instance.connection;
|
|
2935
2611
|
if (conn == null || !conn.isConnected) {
|
|
2936
|
-
return
|
|
2612
|
+
return err3(new VexNotConnectedError);
|
|
2937
2613
|
}
|
|
2938
2614
|
return conn.request(new WriteKeyValueH2DPacket(key, value), WriteKeyValueReplyD2HPacket).map(() => {
|
|
2939
2615
|
return;
|
|
@@ -2941,19 +2617,19 @@ function setValue(state, key, value) {
|
|
|
2941
2617
|
})());
|
|
2942
2618
|
}
|
|
2943
2619
|
function listFiles(state, vendor = 1 /* USER */) {
|
|
2944
|
-
return new
|
|
2620
|
+
return new ResultAsync3((async () => {
|
|
2945
2621
|
const conn = state._instance.connection;
|
|
2946
2622
|
if (conn == null || !conn.isConnected) {
|
|
2947
|
-
return
|
|
2623
|
+
return err3(new VexNotConnectedError);
|
|
2948
2624
|
}
|
|
2949
2625
|
const countResult = await conn.request(new GetDirectoryFileCountH2DPacket(vendor), GetDirectoryFileCountReplyD2HPacket);
|
|
2950
2626
|
if (countResult.isErr())
|
|
2951
|
-
return
|
|
2627
|
+
return err3(countResult.error);
|
|
2952
2628
|
const files = [];
|
|
2953
2629
|
for (let i = 0;i < countResult.value.count; i++) {
|
|
2954
2630
|
const entryResult = await conn.request(new GetDirectoryEntryH2DPacket(i), GetDirectoryEntryReplyD2HPacket);
|
|
2955
2631
|
if (entryResult.isErr())
|
|
2956
|
-
return
|
|
2632
|
+
return err3(entryResult.error);
|
|
2957
2633
|
if (entryResult.value.file != null) {
|
|
2958
2634
|
files.push({
|
|
2959
2635
|
filename: entryResult.value.file.filename,
|
|
@@ -2967,18 +2643,18 @@ function listFiles(state, vendor = 1 /* USER */) {
|
|
|
2967
2643
|
});
|
|
2968
2644
|
}
|
|
2969
2645
|
}
|
|
2970
|
-
return
|
|
2646
|
+
return ok3(files);
|
|
2971
2647
|
})());
|
|
2972
2648
|
}
|
|
2973
2649
|
function listProgram(state) {
|
|
2974
|
-
return new
|
|
2650
|
+
return new ResultAsync3((async () => {
|
|
2975
2651
|
const conn = state._instance.connection;
|
|
2976
2652
|
if (conn == null || !conn.isConnected) {
|
|
2977
|
-
return
|
|
2653
|
+
return err3(new VexNotConnectedError);
|
|
2978
2654
|
}
|
|
2979
2655
|
const files = await listFiles(state, 1 /* USER */);
|
|
2980
2656
|
if (files.isErr())
|
|
2981
|
-
return
|
|
2657
|
+
return err3(files.error);
|
|
2982
2658
|
const programList = [];
|
|
2983
2659
|
const iniFiles = files.value.filter((file) => file.filename.endsWith(".ini"));
|
|
2984
2660
|
for (let i = 0;i < iniFiles.length; i++) {
|
|
@@ -3006,14 +2682,14 @@ function listProgram(state) {
|
|
|
3006
2682
|
}
|
|
3007
2683
|
programList.push(program);
|
|
3008
2684
|
}
|
|
3009
|
-
return
|
|
2685
|
+
return ok3(programList);
|
|
3010
2686
|
})());
|
|
3011
2687
|
}
|
|
3012
2688
|
function readFile(state, request, downloadTarget = 1 /* FILE_TARGET_QSPI */, progressCallback) {
|
|
3013
|
-
return new
|
|
2689
|
+
return new ResultAsync3((async () => {
|
|
3014
2690
|
const conn = state._instance.connection;
|
|
3015
2691
|
if (conn == null || !conn.isConnected) {
|
|
3016
|
-
return
|
|
2692
|
+
return err3(new VexNotConnectedError);
|
|
3017
2693
|
}
|
|
3018
2694
|
let handle;
|
|
3019
2695
|
if (typeof request === "string") {
|
|
@@ -3025,31 +2701,31 @@ function readFile(state, request, downloadTarget = 1 /* FILE_TARGET_QSPI */, pro
|
|
|
3025
2701
|
})());
|
|
3026
2702
|
}
|
|
3027
2703
|
function removeFile(state, request) {
|
|
3028
|
-
return new
|
|
2704
|
+
return new ResultAsync3((async () => {
|
|
3029
2705
|
const conn = state._instance.connection;
|
|
3030
2706
|
if (conn == null || !conn.isConnected) {
|
|
3031
|
-
return
|
|
2707
|
+
return err3(new VexNotConnectedError);
|
|
3032
2708
|
}
|
|
3033
2709
|
return state.withRefreshPaused(() => conn.removeFile(request));
|
|
3034
2710
|
})());
|
|
3035
2711
|
}
|
|
3036
2712
|
function removeAllFiles(state) {
|
|
3037
|
-
return new
|
|
2713
|
+
return new ResultAsync3((async () => {
|
|
3038
2714
|
const conn = state._instance.connection;
|
|
3039
2715
|
if (conn == null || !conn.isConnected) {
|
|
3040
|
-
return
|
|
2716
|
+
return err3(new VexNotConnectedError);
|
|
3041
2717
|
}
|
|
3042
2718
|
return state.withRefreshPaused(() => conn.removeAllFiles());
|
|
3043
2719
|
})());
|
|
3044
2720
|
}
|
|
3045
2721
|
function uploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback) {
|
|
3046
|
-
return new
|
|
2722
|
+
return new ResultAsync3(runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback));
|
|
3047
2723
|
}
|
|
3048
2724
|
async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback) {
|
|
3049
2725
|
const device = state._instance;
|
|
3050
2726
|
const conn = device.connection;
|
|
3051
2727
|
if (conn == null || !conn.isConnected) {
|
|
3052
|
-
return
|
|
2728
|
+
return err3(new VexNotConnectedError);
|
|
3053
2729
|
}
|
|
3054
2730
|
let switchedToDownload = false;
|
|
3055
2731
|
return state.withRefreshPaused(async () => {
|
|
@@ -3058,46 +2734,46 @@ async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progr
|
|
|
3058
2734
|
await sleep(250);
|
|
3059
2735
|
const refreshed = await device.refresh();
|
|
3060
2736
|
if (refreshed.isErr() || !refreshed.value) {
|
|
3061
|
-
return
|
|
2737
|
+
return err3(new VexProtocolError("device is unavailable"));
|
|
3062
2738
|
}
|
|
3063
2739
|
progressCallback("CHANNEL", 0, 1);
|
|
3064
2740
|
const p1 = await device.radio.changeChannel(1 /* DOWNLOAD */);
|
|
3065
2741
|
if (p1.isErr())
|
|
3066
|
-
return
|
|
2742
|
+
return err3(p1.error);
|
|
3067
2743
|
switchedToDownload = true;
|
|
3068
2744
|
await sleep(250);
|
|
3069
2745
|
const transferred = await sleepUntilAsync(async () => (await conn?.getSystemStatus(150))?.isOk() ?? false, 1e4, 200);
|
|
3070
2746
|
if (transferred.isErr())
|
|
3071
|
-
return
|
|
2747
|
+
return err3(transferred.error);
|
|
3072
2748
|
if (!transferred.value) {
|
|
3073
|
-
return
|
|
2749
|
+
return err3(new VexProtocolError("channel switch timed out"));
|
|
3074
2750
|
}
|
|
3075
2751
|
progressCallback("CHANNEL", 1, 1);
|
|
3076
2752
|
}
|
|
3077
2753
|
const p2 = await conn.uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback);
|
|
3078
2754
|
if (p2.isErr())
|
|
3079
|
-
return
|
|
2755
|
+
return err3(p2.error);
|
|
3080
2756
|
if (!p2.value)
|
|
3081
|
-
return
|
|
2757
|
+
return err3(new VexProtocolError("program upload rejected"));
|
|
3082
2758
|
if (device.isV5Controller) {
|
|
3083
2759
|
if (!device.brain.isAvailable) {
|
|
3084
|
-
return
|
|
2760
|
+
return err3(new VexProtocolError("brain unavailable after upload"));
|
|
3085
2761
|
}
|
|
3086
2762
|
progressCallback("CHANNEL", 0, 1);
|
|
3087
2763
|
const p3 = await device.radio.changeChannel(0 /* PIT */);
|
|
3088
2764
|
if (p3.isErr())
|
|
3089
|
-
return
|
|
2765
|
+
return err3(p3.error);
|
|
3090
2766
|
switchedToDownload = false;
|
|
3091
2767
|
await sleep(250);
|
|
3092
2768
|
const transferred = await sleepUntilAsync(async () => (await conn?.getSystemStatus(150))?.isOk() ?? false, 1e4, 200);
|
|
3093
2769
|
if (transferred.isErr())
|
|
3094
|
-
return
|
|
2770
|
+
return err3(transferred.error);
|
|
3095
2771
|
if (!transferred.value) {
|
|
3096
|
-
return
|
|
2772
|
+
return err3(new VexProtocolError("channel switch timed out"));
|
|
3097
2773
|
}
|
|
3098
2774
|
progressCallback("CHANNEL", 1, 1);
|
|
3099
2775
|
}
|
|
3100
|
-
return
|
|
2776
|
+
return ok3(true);
|
|
3101
2777
|
} finally {
|
|
3102
2778
|
if (switchedToDownload) {
|
|
3103
2779
|
await device.radio.changeChannel(0 /* PIT */);
|
|
@@ -3106,19 +2782,19 @@ async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progr
|
|
|
3106
2782
|
});
|
|
3107
2783
|
}
|
|
3108
2784
|
function writeFile(state, request, progressCallback) {
|
|
3109
|
-
return new
|
|
2785
|
+
return new ResultAsync3((async () => {
|
|
3110
2786
|
const conn = state._instance.connection;
|
|
3111
2787
|
if (conn == null || !conn.isConnected) {
|
|
3112
|
-
return
|
|
2788
|
+
return err3(new VexNotConnectedError);
|
|
3113
2789
|
}
|
|
3114
2790
|
return state.withRefreshPaused(() => conn.uploadFileToDevice(request, progressCallback));
|
|
3115
2791
|
})());
|
|
3116
2792
|
}
|
|
3117
2793
|
function captureScreen(state, progressCallback) {
|
|
3118
|
-
return new
|
|
2794
|
+
return new ResultAsync3((async () => {
|
|
3119
2795
|
const conn = state._instance.connection;
|
|
3120
2796
|
if (conn == null || !conn.isConnected) {
|
|
3121
|
-
return
|
|
2797
|
+
return err3(new VexNotConnectedError);
|
|
3122
2798
|
}
|
|
3123
2799
|
return state.withRefreshPaused(() => conn.captureScreen(progressCallback));
|
|
3124
2800
|
})());
|
|
@@ -3232,42 +2908,42 @@ class V5Brain {
|
|
|
3232
2908
|
this.setActiveProgram(value).mapErr(() => {});
|
|
3233
2909
|
}
|
|
3234
2910
|
setActiveProgram(value) {
|
|
3235
|
-
return new
|
|
2911
|
+
return new ResultAsync4((async () => {
|
|
3236
2912
|
if (this.state.brain.activeProgram === value)
|
|
3237
|
-
return
|
|
2913
|
+
return ok4(undefined);
|
|
3238
2914
|
const conn = this.state._instance.connection;
|
|
3239
2915
|
if (conn == null)
|
|
3240
|
-
return
|
|
2916
|
+
return err4(new VexNotConnectedError);
|
|
3241
2917
|
const result = value === 0 ? await conn.stopProgram() : await conn.loadProgram(value);
|
|
3242
2918
|
if (result.isErr())
|
|
3243
|
-
return
|
|
2919
|
+
return err4(result.error);
|
|
3244
2920
|
this.state.brain.activeProgram = value;
|
|
3245
|
-
return
|
|
2921
|
+
return ok4(undefined);
|
|
3246
2922
|
})());
|
|
3247
2923
|
}
|
|
3248
2924
|
runProgram(slot) {
|
|
3249
|
-
return new
|
|
2925
|
+
return new ResultAsync4((async () => {
|
|
3250
2926
|
const conn = this.state._instance.connection;
|
|
3251
2927
|
if (conn == null)
|
|
3252
|
-
return
|
|
2928
|
+
return err4(new VexNotConnectedError);
|
|
3253
2929
|
const reply = await conn.runProgram(slot);
|
|
3254
2930
|
if (reply.isErr())
|
|
3255
|
-
return
|
|
2931
|
+
return err4(reply.error);
|
|
3256
2932
|
if (typeof slot === "number")
|
|
3257
2933
|
this.state.brain.activeProgram = slot;
|
|
3258
|
-
return
|
|
2934
|
+
return ok4(undefined);
|
|
3259
2935
|
})());
|
|
3260
2936
|
}
|
|
3261
2937
|
stopProgram() {
|
|
3262
|
-
return new
|
|
2938
|
+
return new ResultAsync4((async () => {
|
|
3263
2939
|
const conn = this.state._instance.connection;
|
|
3264
2940
|
if (conn == null)
|
|
3265
|
-
return
|
|
2941
|
+
return err4(new VexNotConnectedError);
|
|
3266
2942
|
const reply = await conn.stopProgram();
|
|
3267
2943
|
if (reply.isErr())
|
|
3268
|
-
return
|
|
2944
|
+
return err4(reply.error);
|
|
3269
2945
|
this.state.brain.activeProgram = 0;
|
|
3270
|
-
return
|
|
2946
|
+
return ok4(undefined);
|
|
3271
2947
|
})());
|
|
3272
2948
|
}
|
|
3273
2949
|
get battery() {
|
|
@@ -3440,10 +3116,10 @@ class V5Radio {
|
|
|
3440
3116
|
return this.state.radio.latency;
|
|
3441
3117
|
}
|
|
3442
3118
|
changeChannel(channel) {
|
|
3443
|
-
return new
|
|
3119
|
+
return new ResultAsync4((async () => {
|
|
3444
3120
|
const conn = this.state._instance.connection;
|
|
3445
3121
|
if (conn == null || !conn.isConnected) {
|
|
3446
|
-
return
|
|
3122
|
+
return err4(new VexNotConnectedError);
|
|
3447
3123
|
}
|
|
3448
3124
|
return conn.request(new FileControlH2DPacket(1, channel), FileControlReplyD2HPacket).map(() => {
|
|
3449
3125
|
return;
|
|
@@ -3453,6 +3129,7 @@ class V5Radio {
|
|
|
3453
3129
|
}
|
|
3454
3130
|
|
|
3455
3131
|
// src/VexDevice.ts
|
|
3132
|
+
import { err as err5, ok as ok5, ResultAsync as ResultAsync5 } from "neverthrow";
|
|
3456
3133
|
function unrefTimerIfPossible(timer) {
|
|
3457
3134
|
if (typeof timer !== "object" || timer === null || !("unref" in timer))
|
|
3458
3135
|
return;
|
|
@@ -3460,6 +3137,11 @@ function unrefTimerIfPossible(timer) {
|
|
|
3460
3137
|
if (typeof unref === "function")
|
|
3461
3138
|
unref.call(timer);
|
|
3462
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
|
+
}
|
|
3463
3145
|
|
|
3464
3146
|
class V5SerialDevice extends VexSerialDevice {
|
|
3465
3147
|
autoReconnect = true;
|
|
@@ -3469,8 +3151,11 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3469
3151
|
_refreshInterval;
|
|
3470
3152
|
state = new V5SerialDeviceState(this);
|
|
3471
3153
|
_disposed = false;
|
|
3154
|
+
_lifecycleGeneration = 0;
|
|
3155
|
+
_disconnectListener;
|
|
3472
3156
|
_refreshGeneration = 0;
|
|
3473
3157
|
_autoRefresh = false;
|
|
3158
|
+
_refreshIntervalMs = 200;
|
|
3474
3159
|
_isLastRefreshComplete = true;
|
|
3475
3160
|
_brain = new V5Brain(this.state);
|
|
3476
3161
|
_controllers = [
|
|
@@ -3479,8 +3164,15 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3479
3164
|
];
|
|
3480
3165
|
_radio = new V5Radio(this.state);
|
|
3481
3166
|
_deviceFacades = [];
|
|
3482
|
-
|
|
3167
|
+
_emitSafely(eventName, data) {
|
|
3168
|
+
try {
|
|
3169
|
+
this.emit(eventName, data);
|
|
3170
|
+
} catch {}
|
|
3171
|
+
}
|
|
3172
|
+
constructor(defaultSerial, options = false) {
|
|
3483
3173
|
super(defaultSerial);
|
|
3174
|
+
const autoRefresh = typeof options === "boolean" ? options : options.autoRefresh ?? false;
|
|
3175
|
+
this.refreshIntervalMs = typeof options === "boolean" ? 200 : options.refreshIntervalMs ?? 200;
|
|
3484
3176
|
this.autoRefresh = autoRefresh;
|
|
3485
3177
|
}
|
|
3486
3178
|
get autoRefresh() {
|
|
@@ -3496,6 +3188,21 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3496
3188
|
this._stopRefreshInterval();
|
|
3497
3189
|
}
|
|
3498
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
|
+
}
|
|
3499
3206
|
_startRefreshInterval() {
|
|
3500
3207
|
if (this._refreshInterval !== undefined || this._disposed)
|
|
3501
3208
|
return;
|
|
@@ -3513,16 +3220,16 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3513
3220
|
try {
|
|
3514
3221
|
const r = await this.refresh();
|
|
3515
3222
|
if (r.isErr())
|
|
3516
|
-
this.
|
|
3223
|
+
this._emitSafely("error", r.error);
|
|
3517
3224
|
} catch (error) {
|
|
3518
|
-
this.
|
|
3225
|
+
this._emitSafely("error", error);
|
|
3519
3226
|
} finally {
|
|
3520
3227
|
this._isLastRefreshComplete = true;
|
|
3521
3228
|
}
|
|
3522
3229
|
})();
|
|
3523
3230
|
}
|
|
3524
3231
|
}
|
|
3525
|
-
},
|
|
3232
|
+
}, this._refreshIntervalMs);
|
|
3526
3233
|
unrefTimerIfPossible(this._refreshInterval);
|
|
3527
3234
|
}
|
|
3528
3235
|
_stopRefreshInterval() {
|
|
@@ -3561,82 +3268,149 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3561
3268
|
this.setMatchMode(value).mapErr(() => {});
|
|
3562
3269
|
}
|
|
3563
3270
|
setMatchMode(mode) {
|
|
3564
|
-
return new
|
|
3271
|
+
return new ResultAsync5((async () => {
|
|
3565
3272
|
const reply = await this.connection?.setMatchMode(mode);
|
|
3566
3273
|
if (reply === undefined)
|
|
3567
|
-
return
|
|
3274
|
+
return err5(new VexNotConnectedError);
|
|
3568
3275
|
if (reply.isErr())
|
|
3569
|
-
return
|
|
3276
|
+
return err5(reply.error);
|
|
3570
3277
|
this.state.matchMode = mode;
|
|
3571
|
-
return
|
|
3278
|
+
return ok5(undefined);
|
|
3572
3279
|
})());
|
|
3573
3280
|
}
|
|
3574
3281
|
get radio() {
|
|
3575
3282
|
return this._radio;
|
|
3576
3283
|
}
|
|
3577
3284
|
mockTouch(x, y, press) {
|
|
3578
|
-
return new
|
|
3285
|
+
return new ResultAsync5((async () => {
|
|
3579
3286
|
const reply = await this.connection?.mockTouch(x, y, press);
|
|
3580
3287
|
if (reply === undefined)
|
|
3581
|
-
return
|
|
3288
|
+
return err5(new VexNotConnectedError);
|
|
3582
3289
|
if (reply.isErr())
|
|
3583
|
-
return
|
|
3584
|
-
return
|
|
3290
|
+
return err5(reply.error);
|
|
3291
|
+
return ok5(undefined);
|
|
3585
3292
|
})());
|
|
3586
3293
|
}
|
|
3587
3294
|
connect(conn) {
|
|
3588
|
-
|
|
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));
|
|
3589
3302
|
}
|
|
3590
|
-
async _connect(conn) {
|
|
3303
|
+
async _connect(conn, generation = this._lifecycleGeneration) {
|
|
3304
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3305
|
+
return this._staleLifecycleResult();
|
|
3306
|
+
}
|
|
3591
3307
|
if (this.isConnected)
|
|
3592
|
-
return
|
|
3308
|
+
return ok5(undefined);
|
|
3593
3309
|
if (conn != null) {
|
|
3594
3310
|
if (!conn.isConnected) {
|
|
3595
3311
|
const opened = await conn.open();
|
|
3312
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3313
|
+
await conn.close();
|
|
3314
|
+
return this._staleLifecycleResult();
|
|
3315
|
+
}
|
|
3596
3316
|
if (opened.isErr() || opened.value !== "opened") {
|
|
3597
|
-
return
|
|
3317
|
+
return err5(new VexIoError("failed to open the supplied connection"));
|
|
3598
3318
|
}
|
|
3599
3319
|
}
|
|
3600
3320
|
const q = await conn.query1();
|
|
3321
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3322
|
+
await conn.close();
|
|
3323
|
+
return this._staleLifecycleResult();
|
|
3324
|
+
}
|
|
3601
3325
|
if (q.isErr()) {
|
|
3602
3326
|
await conn.close();
|
|
3603
|
-
return
|
|
3327
|
+
return err5(q.error);
|
|
3328
|
+
}
|
|
3329
|
+
if (!this._commitConnection(conn, generation)) {
|
|
3330
|
+
await conn.close();
|
|
3331
|
+
return this._staleLifecycleResult();
|
|
3604
3332
|
}
|
|
3605
|
-
this.connection = conn;
|
|
3606
3333
|
} else {
|
|
3607
3334
|
let tryIdx = 0;
|
|
3335
|
+
let canRequestPort = true;
|
|
3336
|
+
const attemptedPorts = new Set;
|
|
3337
|
+
const attemptedPortNames = [];
|
|
3608
3338
|
while (true) {
|
|
3609
|
-
|
|
3610
|
-
|
|
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
|
+
}
|
|
3611
3356
|
if (result.isErr()) {
|
|
3612
3357
|
await c.close();
|
|
3613
|
-
return
|
|
3358
|
+
return err5(result.error);
|
|
3614
3359
|
}
|
|
3615
3360
|
if (result.value === "no-port") {
|
|
3616
|
-
|
|
3361
|
+
const attempted = attemptedPortNames.length ? `; attempted ${attemptedPortNames.join(", ")}` : "";
|
|
3362
|
+
return err5(new VexNotConnectedError(`no responsive V5 device was found${attempted}`));
|
|
3617
3363
|
}
|
|
3618
3364
|
if (result.value === "busy") {
|
|
3619
3365
|
await c.close();
|
|
3620
|
-
|
|
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);
|
|
3621
3379
|
}
|
|
3622
3380
|
const q = await c.query1();
|
|
3381
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3382
|
+
await c.close();
|
|
3383
|
+
return this._staleLifecycleResult();
|
|
3384
|
+
}
|
|
3623
3385
|
if (q.isErr()) {
|
|
3624
3386
|
await c.close();
|
|
3625
3387
|
continue;
|
|
3626
3388
|
}
|
|
3627
|
-
this.
|
|
3389
|
+
if (!this._commitConnection(c, generation)) {
|
|
3390
|
+
await c.close();
|
|
3391
|
+
return this._staleLifecycleResult();
|
|
3392
|
+
}
|
|
3628
3393
|
break;
|
|
3629
3394
|
}
|
|
3630
3395
|
}
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
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);
|
|
3635
3406
|
}
|
|
3636
3407
|
async disconnect() {
|
|
3408
|
+
this._lifecycleGeneration++;
|
|
3409
|
+
this._refreshGeneration++;
|
|
3637
3410
|
this._isDisconnecting = true;
|
|
3638
3411
|
const connection = this.connection;
|
|
3639
3412
|
this.connection = undefined;
|
|
3413
|
+
this._detachDisconnectListener();
|
|
3640
3414
|
try {
|
|
3641
3415
|
await connection?.close();
|
|
3642
3416
|
} finally {
|
|
@@ -3650,13 +3424,25 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3650
3424
|
await this.disconnect();
|
|
3651
3425
|
}
|
|
3652
3426
|
reconnect(timeout = 0) {
|
|
3653
|
-
|
|
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));
|
|
3654
3437
|
}
|
|
3655
|
-
async _reconnect(timeout) {
|
|
3438
|
+
async _reconnect(timeout, generation = this._lifecycleGeneration) {
|
|
3439
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3440
|
+
return this._staleLifecycleResult();
|
|
3441
|
+
}
|
|
3656
3442
|
if (this.isConnected)
|
|
3657
|
-
return
|
|
3443
|
+
return ok5(undefined);
|
|
3658
3444
|
if (timeout < 0) {
|
|
3659
|
-
return
|
|
3445
|
+
return err5(new VexInvalidArgumentError("timeout must be non-negative"));
|
|
3660
3446
|
}
|
|
3661
3447
|
const endTime = Date.now() + timeout;
|
|
3662
3448
|
if (this._isReconnecting) {
|
|
@@ -3665,22 +3451,32 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3665
3451
|
} else {
|
|
3666
3452
|
const waited = await sleepUntil(() => !this._isReconnecting, timeout);
|
|
3667
3453
|
if (waited.isErr() || !waited.value) {
|
|
3668
|
-
return
|
|
3454
|
+
return err5(new VexNotConnectedError);
|
|
3669
3455
|
}
|
|
3670
3456
|
}
|
|
3457
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3458
|
+
return this._staleLifecycleResult();
|
|
3459
|
+
}
|
|
3671
3460
|
if (this.isConnected)
|
|
3672
|
-
return
|
|
3461
|
+
return ok5(undefined);
|
|
3673
3462
|
}
|
|
3674
3463
|
this._isReconnecting = true;
|
|
3675
3464
|
try {
|
|
3676
3465
|
while (timeout === 0 || Date.now() < endTime) {
|
|
3677
3466
|
let tryIdx = 0;
|
|
3678
3467
|
while (true) {
|
|
3679
|
-
|
|
3468
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3469
|
+
return this._staleLifecycleResult();
|
|
3470
|
+
}
|
|
3471
|
+
const c = this.createConnection();
|
|
3680
3472
|
const result = await c.open(tryIdx++, false);
|
|
3473
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3474
|
+
await c.close();
|
|
3475
|
+
return this._staleLifecycleResult();
|
|
3476
|
+
}
|
|
3681
3477
|
if (result.isErr()) {
|
|
3682
3478
|
await c.close();
|
|
3683
|
-
return
|
|
3479
|
+
return err5(result.error);
|
|
3684
3480
|
}
|
|
3685
3481
|
if (result.value === "no-port")
|
|
3686
3482
|
break;
|
|
@@ -3689,6 +3485,10 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3689
3485
|
continue;
|
|
3690
3486
|
}
|
|
3691
3487
|
const status = await c.getSystemStatus(200);
|
|
3488
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3489
|
+
await c.close();
|
|
3490
|
+
return this._staleLifecycleResult();
|
|
3491
|
+
}
|
|
3692
3492
|
if (status.isErr()) {
|
|
3693
3493
|
await c.close();
|
|
3694
3494
|
continue;
|
|
@@ -3697,24 +3497,39 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3697
3497
|
await c.close();
|
|
3698
3498
|
continue;
|
|
3699
3499
|
}
|
|
3700
|
-
this.
|
|
3500
|
+
if (!this._commitConnection(c, generation)) {
|
|
3501
|
+
await c.close();
|
|
3502
|
+
return this._staleLifecycleResult();
|
|
3503
|
+
}
|
|
3701
3504
|
break;
|
|
3702
3505
|
}
|
|
3703
3506
|
if (this.isConnected)
|
|
3704
3507
|
break;
|
|
3705
3508
|
const getPortCount = async () => (await this.defaultSerial.getPorts()).length;
|
|
3706
3509
|
const portsCount = await getPortCount();
|
|
3510
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3511
|
+
return this._staleLifecycleResult();
|
|
3512
|
+
}
|
|
3707
3513
|
await sleepUntilAsync(async () => await getPortCount() !== portsCount, 1000);
|
|
3514
|
+
if (!this._isLifecycleCurrent(generation)) {
|
|
3515
|
+
return this._staleLifecycleResult();
|
|
3516
|
+
}
|
|
3708
3517
|
}
|
|
3709
3518
|
} catch (e) {
|
|
3710
|
-
return
|
|
3519
|
+
return err5(toVexSerialError(e));
|
|
3711
3520
|
} finally {
|
|
3712
3521
|
this._isReconnecting = false;
|
|
3713
3522
|
}
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
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);
|
|
3718
3533
|
}
|
|
3719
3534
|
async waitForReconnectToFinish() {
|
|
3720
3535
|
while (this._isReconnecting) {
|
|
@@ -3723,61 +3538,96 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3723
3538
|
return;
|
|
3724
3539
|
}
|
|
3725
3540
|
}
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
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) {
|
|
3731
3568
|
return;
|
|
3732
|
-
this.emit("disconnected", undefined);
|
|
3733
|
-
if (this.autoReconnect) {
|
|
3734
|
-
this.reconnect().mapErr((e) => this.emit("error", e));
|
|
3735
3569
|
}
|
|
3736
|
-
|
|
3737
|
-
|
|
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();
|
|
3738
3604
|
}
|
|
3739
3605
|
refresh() {
|
|
3740
|
-
return new
|
|
3606
|
+
return new ResultAsync5(this._refresh());
|
|
3741
3607
|
}
|
|
3742
3608
|
async _refresh() {
|
|
3743
3609
|
if (this._disposed)
|
|
3744
|
-
return
|
|
3610
|
+
return ok5(false);
|
|
3745
3611
|
const generation = ++this._refreshGeneration;
|
|
3746
3612
|
const conn = this.connection;
|
|
3747
3613
|
if (conn == null || !conn.isConnected) {
|
|
3748
3614
|
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
3749
|
-
return
|
|
3750
|
-
}
|
|
3751
|
-
const ssPacket = await
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
}
|
|
3758
|
-
const sfPacket = await conn.getSystemFlags();
|
|
3759
|
-
if (generation !== this._refreshGeneration || this._disposed)
|
|
3760
|
-
return ok(false);
|
|
3761
|
-
if (sfPacket.isErr()) {
|
|
3762
|
-
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
3763
|
-
return ok(false);
|
|
3764
|
-
}
|
|
3765
|
-
const rdPacket = await conn.getRadioStatus();
|
|
3766
|
-
if (generation !== this._refreshGeneration || this._disposed)
|
|
3767
|
-
return ok(false);
|
|
3768
|
-
if (rdPacket.isErr()) {
|
|
3769
|
-
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
3770
|
-
return ok(false);
|
|
3771
|
-
}
|
|
3772
|
-
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
|
+
]);
|
|
3773
3623
|
if (generation !== this._refreshGeneration || this._disposed)
|
|
3774
|
-
return
|
|
3775
|
-
if (dsPacket.isErr()) {
|
|
3624
|
+
return ok5(false);
|
|
3625
|
+
if (ssPacket.isErr() || sfPacket.isErr() || rdPacket.isErr() || dsPacket.isErr()) {
|
|
3776
3626
|
this._applySnapshotIfCurrent(generation, { isAvailable: false });
|
|
3777
|
-
return
|
|
3627
|
+
return ok5(false);
|
|
3778
3628
|
}
|
|
3779
3629
|
const snapshot = this._buildSnapshot(ssPacket.value, sfPacket.value, rdPacket.value, dsPacket.value);
|
|
3780
|
-
return
|
|
3630
|
+
return ok5(this._applySnapshotIfCurrent(generation, snapshot));
|
|
3781
3631
|
}
|
|
3782
3632
|
_buildSnapshot(ssPacket, sfPacket, rdPacket, dsPacket) {
|
|
3783
3633
|
const flags2 = ssPacket.sysflags[2];
|
|
@@ -3829,7 +3679,7 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3829
3679
|
{
|
|
3830
3680
|
battery: controller1Battery,
|
|
3831
3681
|
isAvailable: controller1Available,
|
|
3832
|
-
isCharging:
|
|
3682
|
+
isCharging: undefined
|
|
3833
3683
|
}
|
|
3834
3684
|
],
|
|
3835
3685
|
radio: {
|
|
@@ -3856,7 +3706,26 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3856
3706
|
}
|
|
3857
3707
|
this.state.matchMode = snapshot.matchMode;
|
|
3858
3708
|
this.state.isFieldControllerConnected = snapshot.isFieldControllerConnected;
|
|
3859
|
-
|
|
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;
|
|
3860
3729
|
Object.assign(this.state.controllers[0], snapshot.controllers[0]);
|
|
3861
3730
|
Object.assign(this.state.controllers[1], snapshot.controllers[1]);
|
|
3862
3731
|
Object.assign(this.state.radio, snapshot.radio);
|
|
@@ -3865,10 +3734,18 @@ class V5SerialDevice extends VexSerialDevice {
|
|
|
3865
3734
|
if (device != null)
|
|
3866
3735
|
next[device.port] = device;
|
|
3867
3736
|
}
|
|
3868
|
-
this.state.devices
|
|
3737
|
+
if (!sameSmartDeviceSlots(this.state.devices, next)) {
|
|
3738
|
+
this.state.devices = next;
|
|
3739
|
+
}
|
|
3869
3740
|
return true;
|
|
3870
3741
|
}
|
|
3871
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
|
+
}
|
|
3872
3749
|
// src/VexIniConfig.ts
|
|
3873
3750
|
class BaseIniBuilder {
|
|
3874
3751
|
str = "";
|
|
@@ -3877,7 +3754,7 @@ class BaseIniBuilder {
|
|
|
3877
3754
|
`;
|
|
3878
3755
|
}
|
|
3879
3756
|
addComment(comment) {
|
|
3880
|
-
this.addLine("; " + comment);
|
|
3757
|
+
this.addLine(comment.length === 0 ? ";" : "; " + comment);
|
|
3881
3758
|
return this;
|
|
3882
3759
|
}
|
|
3883
3760
|
getContent() {
|
|
@@ -3895,18 +3772,23 @@ class IniSectionBuilder extends BaseIniBuilder {
|
|
|
3895
3772
|
this.object = object;
|
|
3896
3773
|
this.keyTransform = keyTransform;
|
|
3897
3774
|
}
|
|
3898
|
-
addSingleObjProperty(key, maxValueLength) {
|
|
3899
|
-
const
|
|
3900
|
-
if (
|
|
3775
|
+
addSingleObjProperty(key, maxValueLength, omitIfEmpty = false) {
|
|
3776
|
+
const rawValue = this.object[key];
|
|
3777
|
+
if (rawValue == null)
|
|
3901
3778
|
return;
|
|
3779
|
+
const value = rawValue.toString();
|
|
3780
|
+
if (omitIfEmpty && value.length === 0)
|
|
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
|
+
}
|
|
3902
3785
|
const formattedKey = this.keyTransform(key).padEnd(12).slice(0, 12);
|
|
3903
|
-
const
|
|
3904
|
-
|
|
3905
|
-
this.addLine(`${formattedKey} = "${escapedVal}"`);
|
|
3786
|
+
const trimmedValue = value.slice(0, maxValueLength);
|
|
3787
|
+
this.addLine(`${formattedKey} = "${trimmedValue}"`);
|
|
3906
3788
|
}
|
|
3907
|
-
addObjProperty(key, maxValueLength) {
|
|
3789
|
+
addObjProperty(key, maxValueLength, omitIfEmpty = false) {
|
|
3908
3790
|
for (const k of Array.isArray(key) ? key : [key]) {
|
|
3909
|
-
this.addSingleObjProperty(k, maxValueLength);
|
|
3791
|
+
this.addSingleObjProperty(k, maxValueLength, omitIfEmpty);
|
|
3910
3792
|
}
|
|
3911
3793
|
return this;
|
|
3912
3794
|
}
|
|
@@ -3924,6 +3806,11 @@ class IniFileBuilder extends BaseIniBuilder {
|
|
|
3924
3806
|
this.str += section.getContent();
|
|
3925
3807
|
return this;
|
|
3926
3808
|
}
|
|
3809
|
+
addOptionalSection(section) {
|
|
3810
|
+
if (section.getContent().length === 0)
|
|
3811
|
+
return this;
|
|
3812
|
+
return this.addComment("").addSection(section);
|
|
3813
|
+
}
|
|
3927
3814
|
}
|
|
3928
3815
|
|
|
3929
3816
|
class ProgramIniConfig {
|
|
@@ -3954,7 +3841,7 @@ class ProgramIniConfig {
|
|
|
3954
3841
|
if (this.program.date.length === 0) {
|
|
3955
3842
|
this.setProgramDate(new Date);
|
|
3956
3843
|
}
|
|
3957
|
-
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();
|
|
3958
3845
|
}
|
|
3959
3846
|
dec2(value) {
|
|
3960
3847
|
return (value % 100).toString(10).padStart(2, "0").toUpperCase();
|
|
@@ -4089,5 +3976,5 @@ export {
|
|
|
4089
3976
|
AckType
|
|
4090
3977
|
};
|
|
4091
3978
|
|
|
4092
|
-
//# debugId=
|
|
3979
|
+
//# debugId=048E789F43ADDE3C64756E2164756E21
|
|
4093
3980
|
//# sourceMappingURL=index.js.map
|