@v5x/serial 0.5.5 → 0.5.7

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