@v5x/serial 0.5.6 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,5 +1,3 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
1
  var __defProp = Object.defineProperty;
4
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -7,30 +5,6 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
5
  function __accessProp(key) {
8
6
  return this[key];
9
7
  }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
- var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
- target = mod != null ? __create(__getProtoOf(mod)) : {};
21
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
- if (mod && typeof mod === "object" || typeof mod === "function") {
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
29
- }
30
- if (canCache)
31
- cache.set(mod, to);
32
- return to;
33
- };
34
8
  var __toCommonJS = (from) => {
35
9
  var entry = (__moduleCache ??= new WeakMap).get(from), desc;
36
10
  if (entry)
@@ -48,7 +22,6 @@ var __toCommonJS = (from) => {
48
22
  return entry;
49
23
  };
50
24
  var __moduleCache;
51
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
52
25
  var __returnValue = (v) => v;
53
26
  function __exportSetter(name, newValue) {
54
27
  this[name] = __returnValue.bind(null, newValue);
@@ -63,483 +36,6 @@ var __export = (target, all) => {
63
36
  });
64
37
  };
65
38
 
66
- // ../../node_modules/.bun/neverthrow@8.2.0/node_modules/neverthrow/dist/index.cjs.js
67
- var require_index_cjs = __commonJS(function(exports2) {
68
- var defaultErrorConfig = {
69
- withStackTrace: false
70
- };
71
- var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
72
- const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
73
- const maybeStack = config.withStackTrace ? new Error().stack : undefined;
74
- return {
75
- data,
76
- message,
77
- stack: maybeStack
78
- };
79
- };
80
- function __awaiter(thisArg, _arguments, P, generator) {
81
- function adopt(value) {
82
- return value instanceof P ? value : new P(function(resolve) {
83
- resolve(value);
84
- });
85
- }
86
- return new (P || (P = Promise))(function(resolve, reject) {
87
- function fulfilled(value) {
88
- try {
89
- step(generator.next(value));
90
- } catch (e) {
91
- reject(e);
92
- }
93
- }
94
- function rejected(value) {
95
- try {
96
- step(generator["throw"](value));
97
- } catch (e) {
98
- reject(e);
99
- }
100
- }
101
- function step(result) {
102
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
103
- }
104
- step((generator = generator.apply(thisArg, _arguments || [])).next());
105
- });
106
- }
107
- function __values(o) {
108
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
109
- if (m)
110
- return m.call(o);
111
- if (o && typeof o.length === "number")
112
- return {
113
- next: function() {
114
- if (o && i >= o.length)
115
- o = undefined;
116
- return { value: o && o[i++], done: !o };
117
- }
118
- };
119
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
120
- }
121
- function __await(v) {
122
- return this instanceof __await ? (this.v = v, this) : new __await(v);
123
- }
124
- function __asyncGenerator(thisArg, _arguments, generator) {
125
- if (!Symbol.asyncIterator)
126
- throw new TypeError("Symbol.asyncIterator is not defined.");
127
- var g = generator.apply(thisArg, _arguments || []), i, q = [];
128
- return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
129
- return this;
130
- }, i;
131
- function awaitReturn(f) {
132
- return function(v) {
133
- return Promise.resolve(v).then(f, reject);
134
- };
135
- }
136
- function verb(n, f) {
137
- if (g[n]) {
138
- i[n] = function(v) {
139
- return new Promise(function(a, b) {
140
- q.push([n, v, a, b]) > 1 || resume(n, v);
141
- });
142
- };
143
- if (f)
144
- i[n] = f(i[n]);
145
- }
146
- }
147
- function resume(n, v) {
148
- try {
149
- step(g[n](v));
150
- } catch (e) {
151
- settle(q[0][3], e);
152
- }
153
- }
154
- function step(r) {
155
- r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
156
- }
157
- function fulfill(value) {
158
- resume("next", value);
159
- }
160
- function reject(value) {
161
- resume("throw", value);
162
- }
163
- function settle(f, v) {
164
- if (f(v), q.shift(), q.length)
165
- resume(q[0][0], q[0][1]);
166
- }
167
- }
168
- function __asyncDelegator(o) {
169
- var i, p;
170
- return i = {}, verb("next"), verb("throw", function(e) {
171
- throw e;
172
- }), verb("return"), i[Symbol.iterator] = function() {
173
- return this;
174
- }, i;
175
- function verb(n, f) {
176
- i[n] = o[n] ? function(v) {
177
- return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
178
- } : f;
179
- }
180
- }
181
- function __asyncValues(o) {
182
- if (!Symbol.asyncIterator)
183
- throw new TypeError("Symbol.asyncIterator is not defined.");
184
- var m = o[Symbol.asyncIterator], i;
185
- 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() {
186
- return this;
187
- }, i);
188
- function verb(n) {
189
- i[n] = o[n] && function(v) {
190
- return new Promise(function(resolve, reject) {
191
- v = o[n](v), settle(resolve, reject, v.done, v.value);
192
- });
193
- };
194
- }
195
- function settle(resolve, reject, d, v) {
196
- Promise.resolve(v).then(function(v2) {
197
- resolve({ value: v2, done: d });
198
- }, reject);
199
- }
200
- }
201
-
202
- class ResultAsync {
203
- constructor(res) {
204
- this._promise = res;
205
- }
206
- static fromSafePromise(promise) {
207
- const newPromise = promise.then((value) => new Ok(value));
208
- return new ResultAsync(newPromise);
209
- }
210
- static fromPromise(promise, errorFn) {
211
- const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
212
- return new ResultAsync(newPromise);
213
- }
214
- static fromThrowable(fn, errorFn) {
215
- return (...args) => {
216
- return new ResultAsync((() => __awaiter(this, undefined, undefined, function* () {
217
- try {
218
- return new Ok(yield fn(...args));
219
- } catch (error) {
220
- return new Err(errorFn ? errorFn(error) : error);
221
- }
222
- }))());
223
- };
224
- }
225
- static combine(asyncResultList) {
226
- return combineResultAsyncList(asyncResultList);
227
- }
228
- static combineWithAllErrors(asyncResultList) {
229
- return combineResultAsyncListWithAllErrors(asyncResultList);
230
- }
231
- map(f) {
232
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
233
- if (res.isErr()) {
234
- return new Err(res.error);
235
- }
236
- return new Ok(yield f(res.value));
237
- })));
238
- }
239
- andThrough(f) {
240
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
241
- if (res.isErr()) {
242
- return new Err(res.error);
243
- }
244
- const newRes = yield f(res.value);
245
- if (newRes.isErr()) {
246
- return new Err(newRes.error);
247
- }
248
- return new Ok(res.value);
249
- })));
250
- }
251
- andTee(f) {
252
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
253
- if (res.isErr()) {
254
- return new Err(res.error);
255
- }
256
- try {
257
- yield f(res.value);
258
- } catch (e) {}
259
- return new Ok(res.value);
260
- })));
261
- }
262
- orTee(f) {
263
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
264
- if (res.isOk()) {
265
- return new Ok(res.value);
266
- }
267
- try {
268
- yield f(res.error);
269
- } catch (e) {}
270
- return new Err(res.error);
271
- })));
272
- }
273
- mapErr(f) {
274
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
275
- if (res.isOk()) {
276
- return new Ok(res.value);
277
- }
278
- return new Err(yield f(res.error));
279
- })));
280
- }
281
- andThen(f) {
282
- return new ResultAsync(this._promise.then((res) => {
283
- if (res.isErr()) {
284
- return new Err(res.error);
285
- }
286
- const newValue = f(res.value);
287
- return newValue instanceof ResultAsync ? newValue._promise : newValue;
288
- }));
289
- }
290
- orElse(f) {
291
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
292
- if (res.isErr()) {
293
- return f(res.error);
294
- }
295
- return new Ok(res.value);
296
- })));
297
- }
298
- match(ok2, _err) {
299
- return this._promise.then((res) => res.match(ok2, _err));
300
- }
301
- unwrapOr(t) {
302
- return this._promise.then((res) => res.unwrapOr(t));
303
- }
304
- safeUnwrap() {
305
- return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
306
- return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
307
- });
308
- }
309
- then(successCallback, failureCallback) {
310
- return this._promise.then(successCallback, failureCallback);
311
- }
312
- [Symbol.asyncIterator]() {
313
- return __asyncGenerator(this, arguments, function* _a() {
314
- const result = yield __await(this._promise);
315
- if (result.isErr()) {
316
- yield yield __await(errAsync(result.error));
317
- }
318
- return yield __await(result.value);
319
- });
320
- }
321
- }
322
- function okAsync(value) {
323
- return new ResultAsync(Promise.resolve(new Ok(value)));
324
- }
325
- function errAsync(err2) {
326
- return new ResultAsync(Promise.resolve(new Err(err2)));
327
- }
328
- var fromPromise = ResultAsync.fromPromise;
329
- var fromSafePromise = ResultAsync.fromSafePromise;
330
- var fromAsyncThrowable = ResultAsync.fromThrowable;
331
- var combineResultList = (resultList) => {
332
- let acc = ok([]);
333
- for (const result of resultList) {
334
- if (result.isErr()) {
335
- acc = err(result.error);
336
- break;
337
- } else {
338
- acc.map((list) => list.push(result.value));
339
- }
340
- }
341
- return acc;
342
- };
343
- var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
344
- var combineResultListWithAllErrors = (resultList) => {
345
- let acc = ok([]);
346
- for (const result of resultList) {
347
- if (result.isErr() && acc.isErr()) {
348
- acc.error.push(result.error);
349
- } else if (result.isErr() && acc.isOk()) {
350
- acc = err([result.error]);
351
- } else if (result.isOk() && acc.isOk()) {
352
- acc.value.push(result.value);
353
- }
354
- }
355
- return acc;
356
- };
357
- var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
358
- exports2.Result = undefined;
359
- (function(Result) {
360
- function fromThrowable2(fn, errorFn) {
361
- return (...args) => {
362
- try {
363
- const result = fn(...args);
364
- return ok(result);
365
- } catch (e) {
366
- return err(errorFn ? errorFn(e) : e);
367
- }
368
- };
369
- }
370
- Result.fromThrowable = fromThrowable2;
371
- function combine(resultList) {
372
- return combineResultList(resultList);
373
- }
374
- Result.combine = combine;
375
- function combineWithAllErrors(resultList) {
376
- return combineResultListWithAllErrors(resultList);
377
- }
378
- Result.combineWithAllErrors = combineWithAllErrors;
379
- })(exports2.Result || (exports2.Result = {}));
380
- function ok(value) {
381
- return new Ok(value);
382
- }
383
- function err(err2) {
384
- return new Err(err2);
385
- }
386
- function safeTry(body) {
387
- const n = body().next();
388
- if (n instanceof Promise) {
389
- return new ResultAsync(n.then((r) => r.value));
390
- }
391
- return n.value;
392
- }
393
-
394
- class Ok {
395
- constructor(value) {
396
- this.value = value;
397
- }
398
- isOk() {
399
- return true;
400
- }
401
- isErr() {
402
- return !this.isOk();
403
- }
404
- map(f) {
405
- return ok(f(this.value));
406
- }
407
- mapErr(_f) {
408
- return ok(this.value);
409
- }
410
- andThen(f) {
411
- return f(this.value);
412
- }
413
- andThrough(f) {
414
- return f(this.value).map((_value) => this.value);
415
- }
416
- andTee(f) {
417
- try {
418
- f(this.value);
419
- } catch (e) {}
420
- return ok(this.value);
421
- }
422
- orTee(_f) {
423
- return ok(this.value);
424
- }
425
- orElse(_f) {
426
- return ok(this.value);
427
- }
428
- asyncAndThen(f) {
429
- return f(this.value);
430
- }
431
- asyncAndThrough(f) {
432
- return f(this.value).map(() => this.value);
433
- }
434
- asyncMap(f) {
435
- return ResultAsync.fromSafePromise(f(this.value));
436
- }
437
- unwrapOr(_v) {
438
- return this.value;
439
- }
440
- match(ok2, _err) {
441
- return ok2(this.value);
442
- }
443
- safeUnwrap() {
444
- const value = this.value;
445
- return function* () {
446
- return value;
447
- }();
448
- }
449
- _unsafeUnwrap(_) {
450
- return this.value;
451
- }
452
- _unsafeUnwrapErr(config) {
453
- throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
454
- }
455
- *[Symbol.iterator]() {
456
- return this.value;
457
- }
458
- }
459
-
460
- class Err {
461
- constructor(error) {
462
- this.error = error;
463
- }
464
- isOk() {
465
- return false;
466
- }
467
- isErr() {
468
- return !this.isOk();
469
- }
470
- map(_f) {
471
- return err(this.error);
472
- }
473
- mapErr(f) {
474
- return err(f(this.error));
475
- }
476
- andThrough(_f) {
477
- return err(this.error);
478
- }
479
- andTee(_f) {
480
- return err(this.error);
481
- }
482
- orTee(f) {
483
- try {
484
- f(this.error);
485
- } catch (e) {}
486
- return err(this.error);
487
- }
488
- andThen(_f) {
489
- return err(this.error);
490
- }
491
- orElse(f) {
492
- return f(this.error);
493
- }
494
- asyncAndThen(_f) {
495
- return errAsync(this.error);
496
- }
497
- asyncAndThrough(_f) {
498
- return errAsync(this.error);
499
- }
500
- asyncMap(_f) {
501
- return errAsync(this.error);
502
- }
503
- unwrapOr(v) {
504
- return v;
505
- }
506
- match(_ok, err2) {
507
- return err2(this.error);
508
- }
509
- safeUnwrap() {
510
- const error = this.error;
511
- return function* () {
512
- yield err(error);
513
- throw new Error("Do not use this generator out of `safeTry`");
514
- }();
515
- }
516
- _unsafeUnwrap(config) {
517
- throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
518
- }
519
- _unsafeUnwrapErr(_) {
520
- return this.error;
521
- }
522
- *[Symbol.iterator]() {
523
- const self = this;
524
- yield self;
525
- return self;
526
- }
527
- }
528
- var fromThrowable = exports2.Result.fromThrowable;
529
- exports2.Err = Err;
530
- exports2.Ok = Ok;
531
- exports2.ResultAsync = ResultAsync;
532
- exports2.err = err;
533
- exports2.errAsync = errAsync;
534
- exports2.fromAsyncThrowable = fromAsyncThrowable;
535
- exports2.fromPromise = fromPromise;
536
- exports2.fromSafePromise = fromSafePromise;
537
- exports2.fromThrowable = fromThrowable;
538
- exports2.ok = ok;
539
- exports2.okAsync = okAsync;
540
- exports2.safeTry = safeTry;
541
- });
542
-
543
39
  // src/index.ts
544
40
  var exports_src = {};
545
41
  __export(exports_src, {
@@ -757,6 +253,7 @@ var AckType;
757
253
  AckType2[AckType2["CDC2_NACK_FILE_SYS_FULL"] = 220] = "CDC2_NACK_FILE_SYS_FULL";
758
254
  AckType2[AckType2["TIMEOUT"] = 256] = "TIMEOUT";
759
255
  AckType2[AckType2["WRITE_ERROR"] = 257] = "WRITE_ERROR";
256
+ AckType2[AckType2["NOT_CONNECTED"] = 258] = "NOT_CONNECTED";
760
257
  })(AckType ||= {});
761
258
  var SmartDeviceType;
762
259
  ((SmartDeviceType2) => {
@@ -939,8 +436,15 @@ class VexEventEmitter {
939
436
  this.handlerMap.set(eventName, listeners);
940
437
  }
941
438
  emit(eventName, data) {
439
+ const listeners = this.handlerMap.get(eventName);
440
+ if (listeners === undefined || listeners.length === 0)
441
+ return;
442
+ if (listeners.length === 1) {
443
+ listeners[0](data);
444
+ return;
445
+ }
942
446
  const errors = [];
943
- for (const callback of [...this.handlerMap.get(eventName) ?? []]) {
447
+ for (const callback of [...listeners]) {
944
448
  try {
945
449
  callback(data);
946
450
  } catch (error) {
@@ -964,24 +468,21 @@ class VexEventTarget {
964
468
  this.emitter = new VexEventEmitter;
965
469
  }
966
470
  emit(eventName, data) {
967
- this.emitter.emit(this.normalizeEventName(eventName), data);
471
+ this.emitter.emit(eventName, data);
968
472
  }
969
473
  on(eventName, listener) {
970
- this.emitter.on(this.normalizeEventName(eventName), listener);
474
+ this.emitter.on(eventName, listener);
971
475
  }
972
476
  remove(eventName, listener) {
973
- this.emitter.remove(this.normalizeEventName(eventName), listener);
477
+ this.emitter.remove(eventName, listener);
974
478
  }
975
479
  clearListeners() {
976
480
  this.emitter.clearListeners();
977
481
  }
978
- normalizeEventName(eventName) {
979
- return String(eventName);
980
- }
981
482
  }
982
483
 
983
484
  // src/VexConnection.ts
984
- var import_neverthrow = __toESM(require_index_cjs(), 1);
485
+ var import_neverthrow = require("neverthrow");
985
486
 
986
487
  // src/VexPacketBase.ts
987
488
  var MATCH_STATUS_ALT_ACK = 167;
@@ -1076,79 +577,115 @@ class CrcGenerator {
1076
577
  }
1077
578
  }
1078
579
 
1079
- // src/VexPacketModels.ts
1080
- var exports_VexPacketModels = {};
1081
- __export(exports_VexPacketModels, {
1082
- WriteKeyValueReplyD2HPacket: () => WriteKeyValueReplyD2HPacket,
1083
- WriteKeyValueH2DPacket: () => WriteKeyValueH2DPacket,
1084
- WriteFileReplyD2HPacket: () => WriteFileReplyD2HPacket,
1085
- WriteFileH2DPacket: () => WriteFileH2DPacket,
1086
- UpdateMatchModeH2DPacket: () => UpdateMatchModeH2DPacket,
1087
- SystemVersionReplyD2HPacket: () => SystemVersionReplyD2HPacket,
1088
- SystemVersionH2DPacket: () => SystemVersionH2DPacket,
1089
- SendDashTouchReplyD2HPacket: () => SendDashTouchReplyD2HPacket,
1090
- SendDashTouchH2DPacket: () => SendDashTouchH2DPacket,
1091
- SelectDashReplyD2HPacket: () => SelectDashReplyD2HPacket,
1092
- SelectDashH2DPacket: () => SelectDashH2DPacket,
1093
- ScreenCaptureReplyD2HPacket: () => ScreenCaptureReplyD2HPacket,
1094
- ScreenCaptureH2DPacket: () => ScreenCaptureH2DPacket,
1095
- ReadLogPageReplyD2HPacket: () => ReadLogPageReplyD2HPacket,
1096
- ReadLogPageH2DPacket: () => ReadLogPageH2DPacket,
1097
- ReadKeyValueReplyD2HPacket: () => ReadKeyValueReplyD2HPacket,
1098
- ReadKeyValueH2DPacket: () => ReadKeyValueH2DPacket,
1099
- ReadFileReplyD2HPacket: () => ReadFileReplyD2HPacket,
1100
- ReadFileH2DPacket: () => ReadFileH2DPacket,
1101
- Query1ReplyD2HPacket: () => Query1ReplyD2HPacket,
1102
- Query1H2DPacket: () => Query1H2DPacket,
1103
- MatchStatusReplyD2HPacket: () => MatchStatusReplyD2HPacket,
1104
- MatchModeReplyD2HPacket: () => MatchModeReplyD2HPacket,
1105
- LoadFileActionReplyD2HPacket: () => LoadFileActionReplyD2HPacket,
1106
- LoadFileActionH2DPacket: () => LoadFileActionH2DPacket,
1107
- LinkFileReplyD2HPacket: () => LinkFileReplyD2HPacket,
1108
- LinkFileH2DPacket: () => LinkFileH2DPacket,
1109
- InitFileTransferReplyD2HPacket: () => InitFileTransferReplyD2HPacket,
1110
- InitFileTransferH2DPacket: () => InitFileTransferH2DPacket,
1111
- GetSystemStatusReplyD2HPacket: () => GetSystemStatusReplyD2HPacket,
1112
- GetSystemStatusH2DPacket: () => GetSystemStatusH2DPacket,
1113
- GetSystemFlagsReplyD2HPacket: () => GetSystemFlagsReplyD2HPacket,
1114
- GetSystemFlagsH2DPacket: () => GetSystemFlagsH2DPacket,
1115
- GetSlot5to8InfoReplyD2HPacket: () => GetSlot5to8InfoReplyD2HPacket,
1116
- GetSlot5to8InfoH2DPacket: () => GetSlot5to8InfoH2DPacket,
1117
- GetSlot1to4InfoReplyD2HPacket: () => GetSlot1to4InfoReplyD2HPacket,
1118
- GetSlot1to4InfoH2DPacket: () => GetSlot1to4InfoH2DPacket,
1119
- GetRadioStatusReplyD2HPacket: () => GetRadioStatusReplyD2HPacket,
1120
- GetRadioStatusH2DPacket: () => GetRadioStatusH2DPacket,
1121
- GetRadioModeH2DPacket: () => GetRadioModeH2DPacket,
1122
- GetProgramSlotInfoReplyD2HPacket: () => GetProgramSlotInfoReplyD2HPacket,
1123
- GetProgramSlotInfoH2DPacket: () => GetProgramSlotInfoH2DPacket,
1124
- GetMatchStatusH2DPacket: () => GetMatchStatusH2DPacket,
1125
- GetLogCountReplyD2HPacket: () => GetLogCountReplyD2HPacket,
1126
- GetLogCountH2DPacket: () => GetLogCountH2DPacket,
1127
- GetFileMetadataReplyD2HPacket: () => GetFileMetadataReplyD2HPacket,
1128
- GetFileMetadataH2DPacket: () => GetFileMetadataH2DPacket,
1129
- GetFdtStatusReplyD2HPacket: () => GetFdtStatusReplyD2HPacket,
1130
- GetFdtStatusH2DPacket: () => GetFdtStatusH2DPacket,
1131
- GetDirectoryFileCountReplyD2HPacket: () => GetDirectoryFileCountReplyD2HPacket,
1132
- GetDirectoryFileCountH2DPacket: () => GetDirectoryFileCountH2DPacket,
1133
- GetDirectoryEntryReplyD2HPacket: () => GetDirectoryEntryReplyD2HPacket,
1134
- GetDirectoryEntryH2DPacket: () => GetDirectoryEntryH2DPacket,
1135
- GetDeviceStatusReplyD2HPacket: () => GetDeviceStatusReplyD2HPacket,
1136
- GetDeviceStatusH2DPacket: () => GetDeviceStatusH2DPacket,
1137
- FileFormatReplyD2HPacket: () => FileFormatReplyD2HPacket,
1138
- FileFormatH2DPacket: () => FileFormatH2DPacket,
1139
- FileControlReplyD2HPacket: () => FileControlReplyD2HPacket,
1140
- FileControlH2DPacket: () => FileControlH2DPacket,
1141
- FileClearUpReplyD2HPacket: () => FileClearUpReplyD2HPacket,
1142
- FileClearUpH2DPacket: () => FileClearUpH2DPacket,
1143
- FactoryStatusReplyD2HPacket: () => FactoryStatusReplyD2HPacket,
1144
- FactoryStatusH2DPacket: () => FactoryStatusH2DPacket,
1145
- FactoryEnableReplyD2HPacket: () => FactoryEnableReplyD2HPacket,
1146
- FactoryEnableH2DPacket: () => FactoryEnableH2DPacket,
1147
- ExitFileTransferReplyD2HPacket: () => ExitFileTransferReplyD2HPacket,
1148
- ExitFileTransferH2DPacket: () => ExitFileTransferH2DPacket,
1149
- EraseFileReplyD2HPacket: () => EraseFileReplyD2HPacket,
1150
- EraseFileH2DPacket: () => EraseFileH2DPacket
1151
- });
580
+ // src/VexPacketEncoder.ts
581
+ var textEncoder = new TextEncoder;
582
+ var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
583
+ function encodeFixedText(value, field, maxBytes) {
584
+ const encoded = textEncoder.encode(value);
585
+ if (encoded.byteLength > maxBytes) {
586
+ throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
587
+ }
588
+ return encoded;
589
+ }
590
+
591
+ class PacketEncoder {
592
+ static HEADERS_LENGTH = 4;
593
+ static HEADER_TO_DEVICE = [201, 54, 184, 71];
594
+ static HEADER_TO_HOST = [170, 85];
595
+ static J2000_EPOCH = 946684800;
596
+ vexVersion = 0;
597
+ crcgen = new CrcGenerator;
598
+ allPacketsTable = new Map;
599
+ static getInstance() {
600
+ Packet.ENCODER ??= new PacketEncoder;
601
+ return Packet.ENCODER;
602
+ }
603
+ constructor() {}
604
+ registerPacketTypes(types) {
605
+ for (const type of types) {
606
+ let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
607
+ if (byExtendedId === undefined) {
608
+ byExtendedId = new Map;
609
+ this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
610
+ }
611
+ byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
612
+ }
613
+ }
614
+ getPacketType(commandId, commandExtendedId) {
615
+ if (commandId === undefined)
616
+ return;
617
+ return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
618
+ }
619
+ createHeader(buf) {
620
+ if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
621
+ buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
622
+ }
623
+ const h = new Uint8Array(buf);
624
+ h.set(HEADER_TO_DEVICE);
625
+ return h;
626
+ }
627
+ cdcCommand(cmd) {
628
+ const h = new Uint8Array(5);
629
+ h.set(HEADER_TO_DEVICE);
630
+ h[4] = cmd;
631
+ return h;
632
+ }
633
+ cdcCommandWithData(cmd, data) {
634
+ const h = new Uint8Array(6 + data.length);
635
+ h.set(HEADER_TO_DEVICE);
636
+ h[4] = cmd;
637
+ h[5] = data.length;
638
+ h.set(data, 6);
639
+ return h;
640
+ }
641
+ cdc2Command(cmd, ext) {
642
+ const h = new Uint8Array(9);
643
+ h.set(HEADER_TO_DEVICE);
644
+ h[4] = cmd;
645
+ h[5] = ext;
646
+ h[6] = 0;
647
+ this.appendCrc16(h);
648
+ return h;
649
+ }
650
+ cdc2CommandBufferLength(data) {
651
+ return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
652
+ }
653
+ cdc2CommandWithData(cmd, ext, data) {
654
+ const h = new Uint8Array(this.cdc2CommandBufferLength(data));
655
+ h.set(HEADER_TO_DEVICE);
656
+ h[4] = cmd;
657
+ h[5] = ext;
658
+ if (data.length < 128) {
659
+ h[6] = data.length;
660
+ h.set(data, 7);
661
+ } else {
662
+ h[6] = data.length >>> 8 | 128;
663
+ h[7] = data.length & 255;
664
+ h.set(data, 8);
665
+ }
666
+ this.appendCrc16(h);
667
+ return h;
668
+ }
669
+ appendCrc16(h) {
670
+ const crc = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
671
+ h[h.length - 2] = crc >>> 8;
672
+ h[h.length - 1] = crc & 255;
673
+ }
674
+ validateHeader(data) {
675
+ return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
676
+ }
677
+ validateMessageCdc(data) {
678
+ const crc = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
679
+ return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc;
680
+ }
681
+ getPayloadSize(data) {
682
+ const a = data[3];
683
+ return (a & 128) === 0 ? a : ((a & 127) << 8) + data[4];
684
+ }
685
+ getHostHeaderLength(data) {
686
+ return (data[3] & 128) === 0 ? 4 : 5;
687
+ }
688
+ }
1152
689
 
1153
690
  // src/VexFirmwareVersion.ts
1154
691
  class VexFirmwareVersion {
@@ -1219,7 +756,7 @@ class PacketView extends DataView {
1219
756
  super(buffer, offset, length);
1220
757
  }
1221
758
  static fromPacket(packet) {
1222
- const view = new PacketView(packet.data.buffer, packet.data.byteOffset);
759
+ const view = new PacketView(packet.data.buffer, packet.data.byteOffset, packet.data.byteLength);
1223
760
  view.position = packet.ackIndex + 1;
1224
761
  return view;
1225
762
  }
@@ -1281,12 +818,12 @@ class PacketView extends DataView {
1281
818
  }
1282
819
 
1283
820
  // src/VexPacketModels.ts
1284
- var textEncoder = new TextEncoder;
821
+ var textEncoder2 = new TextEncoder;
1285
822
  function filePayload(a, b, fileName) {
1286
823
  const payload = new Uint8Array(26);
1287
824
  payload[0] = a;
1288
825
  payload[1] = b;
1289
- payload.set(encodeFixedText(fileName, "Filename", 23), 2);
826
+ payload.set(encodeFixedText(fileName, "Filename", 24), 2);
1290
827
  return payload;
1291
828
  }
1292
829
  var clamp100 = (value) => value !== undefined && value > 100 ? 100 : value;
@@ -1354,7 +891,7 @@ class InitFileTransferH2DPacket extends DeviceBoundPacket {
1354
891
  const timestamp = (Date.now() / 1000 >>> 0) - PacketEncoder.J2000_EPOCH;
1355
892
  view.setUint32(20, timestamp, true);
1356
893
  payload.set(version.toUint8Array(), 24);
1357
- payload.set(encodeFixedText(name, "Filename", 23), 28);
894
+ payload.set(encodeFixedText(name, "Filename", 24), 28);
1358
895
  super(payload);
1359
896
  }
1360
897
  }
@@ -1549,7 +1086,7 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
1549
1086
  static COMMAND_EXTENDED_ID = 47;
1550
1087
  constructor(key, value) {
1551
1088
  const strk = encodeFixedText(key, "Key", 31);
1552
- const strv = textEncoder.encode(value);
1089
+ const strv = textEncoder2.encode(value);
1553
1090
  if (strk.byteLength + strv.byteLength + 20 > 32767) {
1554
1091
  throw new RangeError("Key and value are too large for a protocol packet");
1555
1092
  }
@@ -1712,7 +1249,7 @@ class ReadFileReplyD2HPacket extends HostBoundPacket {
1712
1249
  const view = PacketView.fromPacket(this);
1713
1250
  this.addr = view.nextUint32();
1714
1251
  this.length = this.payloadSize - 8;
1715
- this.buf = this.data.slice(view.position, view.position + this.length).buffer;
1252
+ this.buf = this.data.subarray(view.position, view.position + this.length);
1716
1253
  }
1717
1254
  }
1718
1255
 
@@ -1994,203 +1531,113 @@ class GetRadioStatusReplyD2HPacket extends HostBoundPacket {
1994
1531
  class ScreenCaptureReplyD2HPacket extends HostBoundPacket {
1995
1532
  static COMMAND_ID = 86;
1996
1533
  static COMMAND_EXTENDED_ID = 40;
1997
- }
1998
-
1999
- class SendDashTouchReplyD2HPacket extends HostBoundPacket {
2000
- static COMMAND_ID = 86;
2001
- static COMMAND_EXTENDED_ID = 42;
2002
- }
2003
-
2004
- class SelectDashReplyD2HPacket extends HostBoundPacket {
2005
- static COMMAND_ID = 86;
2006
- static COMMAND_EXTENDED_ID = 43;
2007
- }
2008
-
2009
- class ReadKeyValueReplyD2HPacket extends HostBoundPacket {
2010
- static COMMAND_ID = 86;
2011
- static COMMAND_EXTENDED_ID = 46;
2012
- value;
2013
- constructor(data) {
2014
- super(data);
2015
- this.value = PacketView.fromPacket(this).nextVarNTBS(255);
2016
- }
2017
- }
2018
-
2019
- class WriteKeyValueReplyD2HPacket extends HostBoundPacket {
2020
- static COMMAND_ID = 86;
2021
- static COMMAND_EXTENDED_ID = 47;
2022
- }
2023
-
2024
- class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
2025
- static COMMAND_ID = 86;
2026
- static COMMAND_EXTENDED_ID = 49;
2027
- slotFlags;
2028
- slots;
2029
- constructor(data, start = 1) {
2030
- super(data);
2031
- const view = PacketView.fromPacket(this);
2032
- this.slotFlags = view.nextUint8();
2033
- this.slots = [];
2034
- for (let i = 0;i < 4; i++) {
2035
- if ((this.slotFlags & 1 << start - 1 + i) === 0)
2036
- continue;
2037
- const icon = view.nextUint16();
2038
- const nameLen = view.nextUint8();
2039
- this.slots.push({
2040
- slot: start + i,
2041
- icon,
2042
- name: view.nextString(nameLen)
2043
- });
2044
- }
2045
- }
2046
- }
2047
-
2048
- class GetSlot5to8InfoReplyD2HPacket extends GetSlot1to4InfoReplyD2HPacket {
2049
- static COMMAND_ID = 86;
2050
- static COMMAND_EXTENDED_ID = 50;
2051
- slotStartIndex = 5;
2052
- constructor(data) {
2053
- super(data, 5);
2054
- }
2055
- }
2056
-
2057
- class FactoryStatusReplyD2HPacket extends HostBoundPacket {
2058
- static COMMAND_ID = 86;
2059
- static COMMAND_EXTENDED_ID = 241;
2060
- status;
2061
- percent;
2062
- constructor(data) {
2063
- super(data);
2064
- const view = PacketView.fromPacket(this);
2065
- this.status = view.nextUint8();
2066
- this.percent = view.nextUint8();
2067
- }
2068
- }
2069
-
2070
- class FactoryEnableReplyD2HPacket extends HostBoundPacket {
2071
- static COMMAND_ID = 86;
2072
- static COMMAND_EXTENDED_ID = 255;
2073
- }
2074
-
2075
- // src/VexPacketEncoder.ts
2076
- var textEncoder2 = new TextEncoder;
2077
- var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
2078
- function encodeFixedText(value, field, maxBytes) {
2079
- const encoded = textEncoder2.encode(value);
2080
- if (encoded.byteLength > maxBytes) {
2081
- throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
2082
- }
2083
- return encoded;
2084
- }
2085
-
2086
- class PacketEncoder {
2087
- static HEADERS_LENGTH = 4;
2088
- static HEADER_TO_DEVICE = [201, 54, 184, 71];
2089
- static HEADER_TO_HOST = [170, 85];
2090
- static J2000_EPOCH = 946684800;
2091
- vexVersion = 0;
2092
- crcgen = new CrcGenerator;
2093
- allPacketsTable = new Map;
2094
- static getInstance() {
2095
- Packet.ENCODER ??= new PacketEncoder;
2096
- return Packet.ENCODER;
2097
- }
2098
- constructor() {
2099
- for (const packet of Object.values(exports_VexPacketModels)) {
2100
- if (typeof packet === "function" && packet.prototype instanceof HostBoundPacket) {
2101
- const type = packet;
2102
- let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
2103
- if (byExtendedId === undefined) {
2104
- byExtendedId = new Map;
2105
- this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
2106
- }
2107
- byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
2108
- }
2109
- }
2110
- }
2111
- getPacketType(commandId, commandExtendedId) {
2112
- if (commandId === undefined)
2113
- return;
2114
- return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
2115
- }
2116
- createHeader(buf) {
2117
- if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
2118
- buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
2119
- }
2120
- const h = new Uint8Array(buf);
2121
- h.set(HEADER_TO_DEVICE);
2122
- return h;
2123
- }
2124
- cdcCommand(cmd) {
2125
- const h = new Uint8Array(5);
2126
- h.set(HEADER_TO_DEVICE);
2127
- h[4] = cmd;
2128
- return h;
2129
- }
2130
- cdcCommandWithData(cmd, data) {
2131
- const h = new Uint8Array(6 + data.length);
2132
- h.set(HEADER_TO_DEVICE);
2133
- h[4] = cmd;
2134
- h[5] = data.length;
2135
- h.set(data, 6);
2136
- return h;
2137
- }
2138
- cdc2Command(cmd, ext) {
2139
- const h = new Uint8Array(9);
2140
- h.set(HEADER_TO_DEVICE);
2141
- h[4] = cmd;
2142
- h[5] = ext;
2143
- h[6] = 0;
2144
- this.appendCrc16(h);
2145
- return h;
2146
- }
2147
- cdc2CommandBufferLength(data) {
2148
- return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
2149
- }
2150
- cdc2CommandWithData(cmd, ext, data) {
2151
- const h = new Uint8Array(this.cdc2CommandBufferLength(data));
2152
- h.set(HEADER_TO_DEVICE);
2153
- h[4] = cmd;
2154
- h[5] = ext;
2155
- if (data.length < 128) {
2156
- h[6] = data.length;
2157
- h.set(data, 7);
2158
- } else {
2159
- h[6] = data.length >>> 8 | 128;
2160
- h[7] = data.length & 255;
2161
- h.set(data, 8);
2162
- }
2163
- this.appendCrc16(h);
2164
- return h;
2165
- }
2166
- appendCrc16(h) {
2167
- const crc = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
2168
- h[h.length - 2] = crc >>> 8;
2169
- h[h.length - 1] = crc & 255;
2170
- }
2171
- validateHeader(data) {
2172
- return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
1534
+ }
1535
+
1536
+ class SendDashTouchReplyD2HPacket extends HostBoundPacket {
1537
+ static COMMAND_ID = 86;
1538
+ static COMMAND_EXTENDED_ID = 42;
1539
+ }
1540
+
1541
+ class SelectDashReplyD2HPacket extends HostBoundPacket {
1542
+ static COMMAND_ID = 86;
1543
+ static COMMAND_EXTENDED_ID = 43;
1544
+ }
1545
+
1546
+ class ReadKeyValueReplyD2HPacket extends HostBoundPacket {
1547
+ static COMMAND_ID = 86;
1548
+ static COMMAND_EXTENDED_ID = 46;
1549
+ value;
1550
+ constructor(data) {
1551
+ super(data);
1552
+ this.value = PacketView.fromPacket(this).nextVarNTBS(255);
2173
1553
  }
2174
- validateMessageCdc(data) {
2175
- const crc = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
2176
- return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc;
1554
+ }
1555
+
1556
+ class WriteKeyValueReplyD2HPacket extends HostBoundPacket {
1557
+ static COMMAND_ID = 86;
1558
+ static COMMAND_EXTENDED_ID = 47;
1559
+ }
1560
+
1561
+ class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
1562
+ static COMMAND_ID = 86;
1563
+ static COMMAND_EXTENDED_ID = 49;
1564
+ slotFlags;
1565
+ slots;
1566
+ constructor(data, start = 1) {
1567
+ super(data);
1568
+ const view = PacketView.fromPacket(this);
1569
+ this.slotFlags = view.nextUint8();
1570
+ this.slots = [];
1571
+ for (let i = 0;i < 4; i++) {
1572
+ if ((this.slotFlags & 1 << start - 1 + i) === 0)
1573
+ continue;
1574
+ const icon = view.nextUint16();
1575
+ const nameLen = view.nextUint8();
1576
+ this.slots.push({
1577
+ slot: start + i,
1578
+ icon,
1579
+ name: view.nextString(nameLen)
1580
+ });
1581
+ }
2177
1582
  }
2178
- getPayloadSize(data) {
2179
- const a = data[3];
2180
- return (a & 128) === 0 ? a : ((a & 127) << 8) + data[4];
1583
+ }
1584
+
1585
+ class GetSlot5to8InfoReplyD2HPacket extends GetSlot1to4InfoReplyD2HPacket {
1586
+ static COMMAND_ID = 86;
1587
+ static COMMAND_EXTENDED_ID = 50;
1588
+ slotStartIndex = 5;
1589
+ constructor(data) {
1590
+ super(data, 5);
2181
1591
  }
2182
- getHostHeaderLength(data) {
2183
- return (data[3] & 128) === 0 ? 4 : 5;
1592
+ }
1593
+
1594
+ class FactoryStatusReplyD2HPacket extends HostBoundPacket {
1595
+ static COMMAND_ID = 86;
1596
+ static COMMAND_EXTENDED_ID = 241;
1597
+ status;
1598
+ percent;
1599
+ constructor(data) {
1600
+ super(data);
1601
+ const view = PacketView.fromPacket(this);
1602
+ this.status = view.nextUint8();
1603
+ this.percent = view.nextUint8();
2184
1604
  }
2185
1605
  }
2186
1606
 
2187
- // src/VexConnection.ts
2188
- var thePacketEncoder = PacketEncoder.getInstance();
1607
+ class FactoryEnableReplyD2HPacket extends HostBoundPacket {
1608
+ static COMMAND_ID = 86;
1609
+ static COMMAND_EXTENDED_ID = 255;
1610
+ }
1611
+
1612
+ // src/VexScreenCapture.ts
2189
1613
  var SCREEN_CAPTURE_HEIGHT = 272;
2190
1614
  var SCREEN_CAPTURE_WIDTH = 480;
2191
1615
  var SCREEN_CAPTURE_CHANNELS = 3;
2192
1616
  var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
2193
1617
  var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
1618
+ var SCREEN_CAPTURE_FRAMEBUFFER_SIZE = SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS;
1619
+ function convertScreenCapture(framebuffer) {
1620
+ if (framebuffer.length !== SCREEN_CAPTURE_FRAMEBUFFER_SIZE) {
1621
+ throw new Error(`bad screen capture framebuffer size: ${framebuffer.length}; expected ${SCREEN_CAPTURE_FRAMEBUFFER_SIZE}`);
1622
+ }
1623
+ const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
1624
+ let source = 0;
1625
+ let target = 0;
1626
+ for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
1627
+ for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
1628
+ pixels[target] = framebuffer[source + 2];
1629
+ pixels[target + 1] = framebuffer[source + 1];
1630
+ pixels[target + 2] = framebuffer[source];
1631
+ source += SCREEN_CAPTURE_MESSAGE_CHANNELS;
1632
+ target += SCREEN_CAPTURE_CHANNELS;
1633
+ }
1634
+ source += (SCREEN_CAPTURE_MESSAGE_WIDTH - SCREEN_CAPTURE_WIDTH) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
1635
+ }
1636
+ return pixels;
1637
+ }
1638
+
1639
+ // src/VexConnection.ts
1640
+ var thePacketEncoder = PacketEncoder.getInstance();
2194
1641
 
2195
1642
  class VexSerialConnection extends VexEventTarget {
2196
1643
  filters = [{ usbVendorId: 10376 }];
@@ -2198,12 +1645,31 @@ class VexSerialConnection extends VexEventTarget {
2198
1645
  reader;
2199
1646
  port;
2200
1647
  serial;
2201
- callbacksQueue = [];
1648
+ pendingCallbacks = new Map;
1649
+ rawCallbacks = {
1650
+ head: undefined,
1651
+ tail: undefined
1652
+ };
1653
+ pendingCommandTails = new Map;
2202
1654
  _onPortDisconnect = null;
2203
1655
  _closePromise = null;
1656
+ _openPromise = null;
1657
+ _isClosing = false;
2204
1658
  _wasConnected = false;
2205
1659
  fileTransferTail = Promise.resolve();
2206
1660
  fileTransferDepth = 0;
1661
+ get callbacksQueue() {
1662
+ const callbacks = [];
1663
+ for (const queue of this.pendingCallbacks.values()) {
1664
+ for (let callback = queue.head;callback; callback = callback.next) {
1665
+ callbacks.push(callback);
1666
+ }
1667
+ }
1668
+ for (let callback = this.rawCallbacks.head;callback; callback = callback.next) {
1669
+ callbacks.push(callback);
1670
+ }
1671
+ return callbacks;
1672
+ }
2207
1673
  get isConnected() {
2208
1674
  return this.port !== undefined && this.reader !== undefined && this.writer !== undefined;
2209
1675
  }
@@ -2215,27 +1681,45 @@ class VexSerialConnection extends VexEventTarget {
2215
1681
  this.serial = serial;
2216
1682
  }
2217
1683
  reportWarning(message, details) {
2218
- this.emit("warning", { message, details });
1684
+ this.emitSafely("warning", {
1685
+ message,
1686
+ details
1687
+ });
1688
+ }
1689
+ emitSafely(eventName, data) {
1690
+ try {
1691
+ this.emit(eventName, data);
1692
+ } catch {}
2219
1693
  }
2220
1694
  async close() {
2221
1695
  if (this._closePromise)
2222
1696
  return this._closePromise;
2223
- if (!this._hasOpenResources())
2224
- return;
2225
- this._closePromise = this._doClose();
1697
+ this._isClosing = true;
1698
+ const closing = this._closeAfterOpen();
1699
+ this._closePromise = closing;
2226
1700
  try {
2227
- await this._closePromise;
1701
+ await closing;
2228
1702
  } finally {
2229
- this._closePromise = null;
1703
+ if (this._closePromise === closing)
1704
+ this._closePromise = null;
1705
+ this._isClosing = false;
2230
1706
  }
2231
1707
  }
1708
+ async _closeAfterOpen() {
1709
+ const opening = this._openPromise;
1710
+ if (opening !== null)
1711
+ await opening;
1712
+ if (!this._hasOpenResources())
1713
+ return;
1714
+ await this._doClose();
1715
+ }
2232
1716
  _hasOpenResources() {
2233
- return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.callbacksQueue.length > 0;
1717
+ return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.hasPendingCallbacks();
2234
1718
  }
2235
1719
  async _doClose() {
2236
- for (const callback of this.callbacksQueue.splice(0)) {
1720
+ for (const callback of this.drainPendingCallbacks()) {
2237
1721
  clearTimeout(callback.timeout);
2238
- callback.callback(255 /* CDC2_NACK */);
1722
+ callback.callback(258 /* NOT_CONNECTED */);
2239
1723
  }
2240
1724
  const onDisconnect = this._onPortDisconnect;
2241
1725
  this._onPortDisconnect = null;
@@ -2284,11 +1768,22 @@ class VexSerialConnection extends VexEventTarget {
2284
1768
  }
2285
1769
  if (this._wasConnected) {
2286
1770
  this._wasConnected = false;
2287
- this.emit("disconnected", undefined);
1771
+ this.emitSafely("disconnected", undefined);
2288
1772
  }
2289
1773
  }
2290
1774
  open(use = 0, askUser = true) {
2291
- return new import_neverthrow.ResultAsync(this._open(use, askUser));
1775
+ if (this._openPromise !== null)
1776
+ return new import_neverthrow.ResultAsync(this._openPromise);
1777
+ const opening = this._open(use, askUser);
1778
+ this._openPromise = opening;
1779
+ opening.then(() => {
1780
+ if (this._openPromise === opening)
1781
+ this._openPromise = null;
1782
+ }, () => {
1783
+ if (this._openPromise === opening)
1784
+ this._openPromise = null;
1785
+ });
1786
+ return new import_neverthrow.ResultAsync(opening);
2292
1787
  }
2293
1788
  async _open(use, askUser) {
2294
1789
  if (this._closePromise !== null)
@@ -2321,38 +1816,63 @@ class VexSerialConnection extends VexEventTarget {
2321
1816
  this.reader = this.port.readable.getReader();
2322
1817
  this.startReader();
2323
1818
  this._wasConnected = true;
2324
- this.emit("connected", undefined);
1819
+ this.emitSafely("connected", undefined);
2325
1820
  return import_neverthrow.ok("opened");
2326
1821
  } catch (e) {
2327
- await this.close();
1822
+ await this._doClose();
2328
1823
  return import_neverthrow.err(toVexSerialError(e, "io"));
2329
1824
  }
2330
1825
  }
2331
1826
  async writeDataAsync(rawData, timeout = 1000) {
1827
+ if (rawData instanceof DeviceBoundPacket) {
1828
+ return this.serializeCommand(rawData.commandId, rawData.commandExtendedId, () => this.writeDataAsyncUnserialized(rawData, timeout));
1829
+ }
1830
+ return this.writeDataAsyncUnserialized(rawData, timeout);
1831
+ }
1832
+ async serializeCommand(commandId, commandExtendedId, operation) {
1833
+ const key = `${commandId}:${commandExtendedId ?? ""}`;
1834
+ const previous = this.pendingCommandTails.get(key) ?? Promise.resolve();
1835
+ let release = () => {};
1836
+ const current = new Promise((resolve) => {
1837
+ release = resolve;
1838
+ });
1839
+ this.pendingCommandTails.set(key, current);
1840
+ await previous;
1841
+ try {
1842
+ return await operation();
1843
+ } finally {
1844
+ release();
1845
+ if (this.pendingCommandTails.get(key) === current) {
1846
+ this.pendingCommandTails.delete(key);
1847
+ }
1848
+ }
1849
+ }
1850
+ async writeDataAsyncUnserialized(rawData, timeout) {
2332
1851
  return new Promise((resolve) => {
2333
- if (this.writer === undefined) {
2334
- resolve(255 /* CDC2_NACK */);
1852
+ if (this.writer === undefined || this._isClosing) {
1853
+ resolve(258 /* NOT_CONNECTED */);
2335
1854
  return;
2336
1855
  }
2337
1856
  const data = rawData instanceof DeviceBoundPacket ? rawData.data : rawData;
1857
+ const queue = rawData instanceof DeviceBoundPacket ? this.getPendingQueue(rawData.commandId, rawData.commandExtendedId, true) : this.rawCallbacks;
2338
1858
  const cb = {
1859
+ active: true,
2339
1860
  callback: resolve,
2340
1861
  timeout: setTimeout(() => {
2341
- const index = this.callbacksQueue.indexOf(cb);
2342
- if (index === -1)
1862
+ if (!this.removePendingCallback(cb))
2343
1863
  return;
2344
- this.callbacksQueue.splice(index, 1);
2345
1864
  cb.callback(256 /* TIMEOUT */);
2346
1865
  }, timeout),
1866
+ next: undefined,
1867
+ previous: undefined,
1868
+ queue,
2347
1869
  wantedCommandId: rawData instanceof DeviceBoundPacket ? rawData.commandId : undefined,
2348
1870
  wantedCommandExId: rawData instanceof DeviceBoundPacket ? rawData.commandExtendedId : undefined
2349
1871
  };
2350
- this.callbacksQueue.push(cb);
1872
+ this.enqueuePendingCallback(cb);
2351
1873
  this.writer.write(data).catch(() => {
2352
- const index = this.callbacksQueue.indexOf(cb);
2353
- if (index === -1)
1874
+ if (!this.removePendingCallback(cb))
2354
1875
  return;
2355
- this.callbacksQueue.splice(index, 1);
2356
1876
  clearTimeout(cb.timeout);
2357
1877
  resolve(257 /* WRITE_ERROR */);
2358
1878
  });
@@ -2363,6 +1883,9 @@ class VexSerialConnection extends VexEventTarget {
2363
1883
  const result = await this.writeDataAsync(packet, timeout);
2364
1884
  if (result instanceof ReplyType)
2365
1885
  return import_neverthrow.ok(result);
1886
+ if (result === 258 /* NOT_CONNECTED */) {
1887
+ return import_neverthrow.err(new VexNotConnectedError);
1888
+ }
2366
1889
  return import_neverthrow.err(new VexProtocolError(expectedReplyMessage(packet, ReplyType, result), typeof result === "number" ? result : undefined));
2367
1890
  })());
2368
1891
  }
@@ -2373,56 +1896,48 @@ class VexSerialConnection extends VexEventTarget {
2373
1896
  const { value: readData, done: isDone } = await this.reader.read();
2374
1897
  if (isDone)
2375
1898
  throw new Error("No data");
2376
- cache = binaryArrayJoin(cache, readData);
1899
+ cache.append(readData);
2377
1900
  }
2378
- return cache;
2379
1901
  }
2380
1902
  async startReader() {
2381
- let cache = new Uint8Array([]);
1903
+ const cache = new ReceiveBuffer;
2382
1904
  let sliceIdx = 0;
2383
1905
  for (;; )
2384
1906
  try {
2385
- cache = await this.readData(cache, 5);
1907
+ await this.readData(cache, 5);
2386
1908
  sliceIdx = 0;
2387
- while (!thePacketEncoder.validateHeader(cache)) {
2388
- const nextHeader = cache.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && cache[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
1909
+ while (!thePacketEncoder.validateHeader(cache.bytes)) {
1910
+ const bytes = cache.bytes;
1911
+ const nextHeader = bytes.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && bytes[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
2389
1912
  if (nextHeader >= 0) {
2390
- cache = cache.slice(nextHeader);
1913
+ cache.discard(nextHeader);
2391
1914
  } else {
2392
- cache = cache.slice(cache.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : cache.length);
1915
+ cache.discard(bytes.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : bytes.length);
2393
1916
  }
2394
- cache = await this.readData(cache, 5);
1917
+ await this.readData(cache, 5);
2395
1918
  }
2396
- const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache);
2397
- const n = thePacketEncoder.getHostHeaderLength(cache);
1919
+ const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache.bytes);
1920
+ const n = thePacketEncoder.getHostHeaderLength(cache.bytes);
2398
1921
  const totalSize = n + payloadExpectedSize;
2399
- cache = await this.readData(cache, totalSize);
1922
+ await this.readData(cache, totalSize);
2400
1923
  sliceIdx = totalSize;
2401
- const cmdId = cache[2];
1924
+ const packet = cache.copy(totalSize);
1925
+ const cmdId = packet[2];
2402
1926
  const hasExtId = cmdId === 88 || cmdId === 86;
2403
- const cmdExId = hasExtId ? cache[n] : undefined;
2404
- const ack = cache[n + 1];
1927
+ const cmdExId = hasExtId ? packet[n] : undefined;
1928
+ const ack = packet[n + 1];
2405
1929
  if (hasExtId) {
2406
- if (!thePacketEncoder.validateMessageCdc(cache))
2407
- throw new Error("Invalid message CDC");
2408
- }
2409
- let matchIdx = -1;
2410
- let untypedIdx = -1;
2411
- for (let i = 0;i < this.callbacksQueue.length; i++) {
2412
- const candidate = this.callbacksQueue[i];
2413
- if (candidate.wantedCommandId === undefined && candidate.wantedCommandExId === undefined) {
2414
- if (untypedIdx === -1)
2415
- untypedIdx = i;
1930
+ if (!thePacketEncoder.validateMessageCdc(packet)) {
1931
+ this.reportWarning("discarding a reply with an invalid CDC CRC", {
1932
+ commandId: cmdId,
1933
+ commandExtendedId: cmdExId,
1934
+ ack
1935
+ });
2416
1936
  continue;
2417
1937
  }
2418
- if ((candidate.wantedCommandId === undefined || candidate.wantedCommandId === cmdId) && (candidate.wantedCommandExId === undefined || candidate.wantedCommandExId === cmdExId)) {
2419
- matchIdx = i;
2420
- break;
2421
- }
2422
1938
  }
2423
- if (matchIdx === -1)
2424
- matchIdx = untypedIdx;
2425
- if (matchIdx === -1) {
1939
+ const callbackInfo = this.shiftPendingCallback(cmdId, cmdExId) ?? this.shiftRawCallback();
1940
+ if (callbackInfo === undefined) {
2426
1941
  this.reportWarning("received a reply with no matching request", {
2427
1942
  commandId: cmdId,
2428
1943
  commandExtendedId: cmdExId,
@@ -2430,38 +1945,117 @@ class VexSerialConnection extends VexEventTarget {
2430
1945
  });
2431
1946
  continue;
2432
1947
  }
2433
- const callbackInfo = this.callbacksQueue[matchIdx];
2434
1948
  const wantedCmdId = callbackInfo.wantedCommandId;
2435
1949
  const wantedCmdExId = callbackInfo.wantedCommandExId;
2436
- const data = cache.slice(0, sliceIdx);
2437
1950
  const PackageType = thePacketEncoder.getPacketType(wantedCmdId, wantedCmdExId);
2438
1951
  if (wantedCmdId === undefined || PackageType === undefined) {
2439
1952
  if (wantedCmdId !== undefined) {
2440
1953
  this.reportWarning("no packet class is registered for the wanted command", { commandId: wantedCmdId, commandExtendedId: wantedCmdExId });
2441
1954
  }
2442
- callbackInfo.callback(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
1955
+ callbackInfo.callback(packet.buffer);
2443
1956
  } else {
2444
- if (!hasExtId || PackageType.isValidPacket(data, n)) {
2445
- callbackInfo.callback(new PackageType(data));
1957
+ if (PackageType.isValidPacket(packet, n)) {
1958
+ callbackInfo.callback(new PackageType(packet));
2446
1959
  } else {
2447
1960
  this.reportWarning("reply failed packet validation; delivering its ack instead", { commandId: cmdId, commandExtendedId: cmdExId, ack });
2448
1961
  callbackInfo.callback(ack);
2449
1962
  }
2450
1963
  }
2451
1964
  clearTimeout(callbackInfo.timeout);
2452
- this.callbacksQueue.splice(matchIdx, 1);
2453
1965
  } catch (e) {
2454
1966
  if (!(e instanceof Error && e.message === "No data")) {
2455
1967
  this.reportWarning("reader loop stopped by a read error", {
2456
1968
  error: e,
2457
- pendingBytes: cache
1969
+ pendingBytes: cache.bytes
2458
1970
  });
2459
1971
  }
2460
1972
  await this.close();
2461
1973
  break;
2462
1974
  } finally {
2463
- cache = cache.slice(sliceIdx);
1975
+ cache.discard(sliceIdx);
1976
+ }
1977
+ }
1978
+ pendingKey(commandId, commandExtendedId) {
1979
+ return `${commandId}:${commandExtendedId ?? ""}`;
1980
+ }
1981
+ getPendingQueue(commandId, commandExtendedId, create) {
1982
+ const key = this.pendingKey(commandId, commandExtendedId);
1983
+ let queue = this.pendingCallbacks.get(key);
1984
+ if (queue === undefined && create) {
1985
+ const created = { head: undefined, tail: undefined };
1986
+ this.pendingCallbacks.set(key, created);
1987
+ queue = created;
1988
+ }
1989
+ return queue ?? { head: undefined, tail: undefined };
1990
+ }
1991
+ enqueuePendingCallback(callback) {
1992
+ const tail = callback.queue.tail;
1993
+ callback.previous = tail;
1994
+ if (tail === undefined)
1995
+ callback.queue.head = callback;
1996
+ else
1997
+ tail.next = callback;
1998
+ callback.queue.tail = callback;
1999
+ }
2000
+ removePendingCallback(callback) {
2001
+ if (!callback.active)
2002
+ return false;
2003
+ callback.active = false;
2004
+ const { queue, previous, next } = callback;
2005
+ if (previous === undefined)
2006
+ queue.head = next;
2007
+ else
2008
+ previous.next = next;
2009
+ if (next === undefined)
2010
+ queue.tail = previous;
2011
+ else
2012
+ next.previous = previous;
2013
+ callback.previous = undefined;
2014
+ callback.next = undefined;
2015
+ if (queue !== this.rawCallbacks && queue.head === undefined && callback.wantedCommandId !== undefined) {
2016
+ this.pendingCallbacks.delete(this.pendingKey(callback.wantedCommandId, callback.wantedCommandExId));
2017
+ }
2018
+ return true;
2019
+ }
2020
+ shiftPendingCallback(commandId, commandExtendedId) {
2021
+ const queue = this.getPendingQueue(commandId, commandExtendedId, false);
2022
+ const callback = queue.head;
2023
+ if (callback !== undefined)
2024
+ this.removePendingCallback(callback);
2025
+ return callback;
2026
+ }
2027
+ shiftRawCallback() {
2028
+ const callback = this.rawCallbacks.head;
2029
+ if (callback !== undefined)
2030
+ this.removePendingCallback(callback);
2031
+ return callback;
2032
+ }
2033
+ hasPendingCallbacks() {
2034
+ return this.pendingCallbacks.size > 0 || this.rawCallbacks.head !== undefined;
2035
+ }
2036
+ drainPendingCallbacks() {
2037
+ const callbacks = [];
2038
+ for (const queue of this.pendingCallbacks.values()) {
2039
+ for (let callback = queue.head;callback; ) {
2040
+ const next = callback.next;
2041
+ callback.active = false;
2042
+ callback.previous = undefined;
2043
+ callback.next = undefined;
2044
+ callbacks.push(callback);
2045
+ callback = next;
2464
2046
  }
2047
+ }
2048
+ for (let callback = this.rawCallbacks.head;callback; ) {
2049
+ const next = callback.next;
2050
+ callback.active = false;
2051
+ callback.previous = undefined;
2052
+ callback.next = undefined;
2053
+ callbacks.push(callback);
2054
+ callback = next;
2055
+ }
2056
+ this.pendingCallbacks.clear();
2057
+ this.rawCallbacks = { head: undefined, tail: undefined };
2058
+ return callbacks;
2465
2059
  }
2466
2060
  query1() {
2467
2061
  return this.request(new Query1H2DPacket, Query1ReplyD2HPacket, 100);
@@ -2579,7 +2173,9 @@ class V5SerialConnection extends VexSerialConnection {
2579
2173
  let bufferOffset = 0;
2580
2174
  const fileBuf = new Uint8Array(fileSize);
2581
2175
  while (bufferOffset < fileSize) {
2582
- const requestedSize = Math.min(bufferChunkSize, fileSize - bufferOffset);
2176
+ const remainingSize = fileSize - bufferOffset;
2177
+ const chunkSize = Math.min(bufferChunkSize, remainingSize);
2178
+ const requestedSize = chunkSize + 3 & ~3;
2583
2179
  const p2Result = await this.request(new ReadFileH2DPacket(nextAddress, requestedSize), ReadFileReplyD2HPacket, 3000);
2584
2180
  if (p2Result.isErr())
2585
2181
  throw p2Result.error;
@@ -2590,9 +2186,10 @@ class V5SerialConnection extends VexSerialConnection {
2590
2186
  if (p2.length <= 0 || p2.length > requestedSize || p2.buf.byteLength !== p2.length) {
2591
2187
  throw new VexTransferError(`ReadFileReplyD2HPacket returned invalid length ${p2.length}`);
2592
2188
  }
2593
- fileBuf.set(new Uint8Array(p2.buf), bufferOffset);
2594
- bufferOffset += p2.length;
2595
- nextAddress += p2.length;
2189
+ const receivedSize = Math.min(p2.length, remainingSize);
2190
+ fileBuf.set(p2.buf.subarray(0, receivedSize), bufferOffset);
2191
+ bufferOffset += receivedSize;
2192
+ nextAddress += receivedSize;
2596
2193
  progressCallback?.(bufferOffset, fileSize);
2597
2194
  }
2598
2195
  transferFailed = false;
@@ -2747,7 +2344,7 @@ class V5SerialConnection extends VexSerialConnection {
2747
2344
  filename: "screen",
2748
2345
  vendor: 15 /* SYS */,
2749
2346
  loadAddress: 0,
2750
- size: SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS
2347
+ size: SCREEN_CAPTURE_FRAMEBUFFER_SIZE
2751
2348
  }, 2 /* FILE_TARGET_CBUF */, progressCallback);
2752
2349
  if (framebuffer.isErr())
2753
2350
  return import_neverthrow.err(framebuffer.error);
@@ -2772,34 +2369,60 @@ class V5SerialConnection extends VexSerialConnection {
2772
2369
  return this.request(new SelectDashH2DPacket(screen, port), SelectDashReplyD2HPacket);
2773
2370
  }
2774
2371
  }
2775
- function binaryArrayJoin(left, right) {
2776
- const leftSize = left != null ? left.byteLength : 0;
2777
- const rightSize = right != null ? right.byteLength : 0;
2778
- const all = new Uint8Array(leftSize + rightSize);
2779
- if (all.length === 0)
2780
- return new Uint8Array;
2781
- if (left != null)
2782
- all.set(new Uint8Array(left), 0);
2783
- if (right != null)
2784
- all.set(new Uint8Array(right), leftSize);
2785
- return all;
2372
+
2373
+ class ReceiveBuffer {
2374
+ storage = new Uint8Array(0);
2375
+ start = 0;
2376
+ end = 0;
2377
+ get byteLength() {
2378
+ return this.end - this.start;
2379
+ }
2380
+ get bytes() {
2381
+ return this.storage.subarray(this.start, this.end);
2382
+ }
2383
+ append(chunk) {
2384
+ this.reserve(chunk.byteLength);
2385
+ this.storage.set(chunk, this.end);
2386
+ this.end += chunk.byteLength;
2387
+ }
2388
+ copy(length) {
2389
+ return this.bytes.slice(0, length);
2390
+ }
2391
+ discard(length) {
2392
+ this.start += length < 0 ? this.byteLength + length : length;
2393
+ if (this.start < 0)
2394
+ this.start = 0;
2395
+ if (this.start > this.end)
2396
+ this.start = this.end;
2397
+ if (this.start === this.end) {
2398
+ this.start = 0;
2399
+ this.end = 0;
2400
+ } else if (this.start >= 4096 && this.start * 2 >= this.storage.length) {
2401
+ this.storage.copyWithin(0, this.start, this.end);
2402
+ this.end -= this.start;
2403
+ this.start = 0;
2404
+ }
2405
+ }
2406
+ reserve(additional) {
2407
+ const required = this.byteLength + additional;
2408
+ if (this.storage.length - this.end >= additional)
2409
+ return;
2410
+ if (this.storage.length >= required) {
2411
+ this.storage.copyWithin(0, this.start, this.end);
2412
+ this.end = this.byteLength;
2413
+ this.start = 0;
2414
+ return;
2415
+ }
2416
+ const storage = new Uint8Array(Math.max(required, Math.max(64, this.storage.length * 2)));
2417
+ storage.set(this.bytes);
2418
+ this.end = this.byteLength;
2419
+ this.start = 0;
2420
+ this.storage = storage;
2421
+ }
2786
2422
  }
2787
2423
  function getTransferChunkSize(windowSize) {
2788
2424
  return windowSize > 0 && windowSize <= USER_PROG_CHUNK_SIZE ? windowSize : USER_PROG_CHUNK_SIZE;
2789
2425
  }
2790
- function convertScreenCapture(framebuffer) {
2791
- const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
2792
- for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
2793
- for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
2794
- const source = (row * SCREEN_CAPTURE_MESSAGE_WIDTH + column) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
2795
- const target = (row * SCREEN_CAPTURE_WIDTH + column) * SCREEN_CAPTURE_CHANNELS;
2796
- pixels[target] = framebuffer[source + 2] ?? 0;
2797
- pixels[target + 1] = framebuffer[source + 1] ?? 0;
2798
- pixels[target + 2] = framebuffer[source] ?? 0;
2799
- }
2800
- }
2801
- return pixels;
2802
- }
2803
2426
  function wrapTransfer(conn, operation) {
2804
2427
  return new import_neverthrow.ResultAsync(conn.withFileTransfer(async () => {
2805
2428
  try {
@@ -2823,10 +2446,10 @@ function ackTypeName(ackType) {
2823
2446
  return `AckType.${AckType[ackType] ?? "UNKNOWN"} (${ackType})`;
2824
2447
  }
2825
2448
  // src/VexDeviceState.ts
2826
- var import_neverthrow4 = __toESM(require_index_cjs(), 1);
2449
+ var import_neverthrow4 = require("neverthrow");
2827
2450
 
2828
2451
  // src/VexFirmware.ts
2829
- var import_neverthrow2 = __toESM(require_index_cjs(), 1);
2452
+ var import_neverthrow2 = require("neverthrow");
2830
2453
  var MAX_CATALOG_BYTES = 4 * 1024;
2831
2454
  var MAX_VEXOS_BYTES = 64 * 1024 * 1024;
2832
2455
  var MAX_FIRMWARE_IMAGE_BYTES = 32 * 1024 * 1024;
@@ -2974,7 +2597,7 @@ async function flashFactoryImage(conn, options, pcb) {
2974
2597
  autoRun: true,
2975
2598
  linkedFile: undefined
2976
2599
  };
2977
- const upload = await conn.uploadFileToDevice(writeRequest, (c, t) => {
2600
+ const upload = await conn.uploadFileToDeviceUnlocked(writeRequest, (c, t) => {
2978
2601
  pcb(`UPLOAD ${label}`, c, t);
2979
2602
  });
2980
2603
  if (upload.isErr())
@@ -3101,34 +2724,40 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
3101
2724
  }
3102
2725
  pcb("UNZIP VEXOS", 1, 1);
3103
2726
  return state.withRefreshPaused(async () => {
3104
- const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
3105
- if (boot === undefined) {
3106
- return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
3107
- }
3108
- const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
3109
- if (assertImage === undefined) {
3110
- return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing assets.bin"));
2727
+ try {
2728
+ return await conn.withFileTransfer(async () => {
2729
+ const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
2730
+ if (boot === undefined) {
2731
+ return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
2732
+ }
2733
+ const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
2734
+ if (assertImage === undefined) {
2735
+ return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing assets.bin"));
2736
+ }
2737
+ const bootFlash = await flashFactoryImage(conn, {
2738
+ image: boot,
2739
+ label: "BOOT",
2740
+ downloadTarget: 14 /* FILE_TARGET_B1 */
2741
+ }, pcb);
2742
+ if (bootFlash.isErr())
2743
+ return import_neverthrow2.err(bootFlash.error);
2744
+ const assetsFlash = await flashFactoryImage(conn, {
2745
+ image: assertImage,
2746
+ label: "ASSETS",
2747
+ downloadTarget: 13 /* FILE_TARGET_A1 */
2748
+ }, pcb);
2749
+ if (assetsFlash.isErr())
2750
+ return import_neverthrow2.err(assetsFlash.error);
2751
+ return import_neverthrow2.ok(true);
2752
+ });
2753
+ } catch (e) {
2754
+ return import_neverthrow2.err(toVexSerialError(e, "firmware"));
3111
2755
  }
3112
- const bootFlash = await flashFactoryImage(conn, {
3113
- image: boot,
3114
- label: "BOOT",
3115
- downloadTarget: 14 /* FILE_TARGET_B1 */
3116
- }, pcb);
3117
- if (bootFlash.isErr())
3118
- return import_neverthrow2.err(bootFlash.error);
3119
- const assetsFlash = await flashFactoryImage(conn, {
3120
- image: assertImage,
3121
- label: "ASSETS",
3122
- downloadTarget: 13 /* FILE_TARGET_A1 */
3123
- }, pcb);
3124
- if (assetsFlash.isErr())
3125
- return import_neverthrow2.err(assetsFlash.error);
3126
- return import_neverthrow2.ok(true);
3127
2756
  });
3128
2757
  }
3129
2758
 
3130
2759
  // src/VexTransfers.ts
3131
- var import_neverthrow3 = __toESM(require_index_cjs(), 1);
2760
+ var import_neverthrow3 = require("neverthrow");
3132
2761
  function getValue(state, key) {
3133
2762
  return new import_neverthrow3.ResultAsync((async () => {
3134
2763
  const conn = state._instance.connection;
@@ -3662,7 +3291,7 @@ class V5Radio {
3662
3291
  }
3663
3292
 
3664
3293
  // src/VexDevice.ts
3665
- var import_neverthrow5 = __toESM(require_index_cjs(), 1);
3294
+ var import_neverthrow5 = require("neverthrow");
3666
3295
  function unrefTimerIfPossible(timer) {
3667
3296
  if (typeof timer !== "object" || timer === null || !("unref" in timer))
3668
3297
  return;
@@ -3670,6 +3299,11 @@ function unrefTimerIfPossible(timer) {
3670
3299
  if (typeof unref === "function")
3671
3300
  unref.call(timer);
3672
3301
  }
3302
+ function describePort(port) {
3303
+ const info = port.getInfo();
3304
+ const identifier = info.path ?? info.id ?? info.serialNumber;
3305
+ return typeof identifier === "string" ? identifier : undefined;
3306
+ }
3673
3307
 
3674
3308
  class V5SerialDevice extends VexSerialDevice {
3675
3309
  autoReconnect = true;
@@ -3679,8 +3313,11 @@ class V5SerialDevice extends VexSerialDevice {
3679
3313
  _refreshInterval;
3680
3314
  state = new V5SerialDeviceState(this);
3681
3315
  _disposed = false;
3316
+ _lifecycleGeneration = 0;
3317
+ _disconnectListener;
3682
3318
  _refreshGeneration = 0;
3683
3319
  _autoRefresh = false;
3320
+ _refreshIntervalMs = 200;
3684
3321
  _isLastRefreshComplete = true;
3685
3322
  _brain = new V5Brain(this.state);
3686
3323
  _controllers = [
@@ -3689,8 +3326,15 @@ class V5SerialDevice extends VexSerialDevice {
3689
3326
  ];
3690
3327
  _radio = new V5Radio(this.state);
3691
3328
  _deviceFacades = [];
3692
- constructor(defaultSerial, autoRefresh = false) {
3329
+ _emitSafely(eventName, data) {
3330
+ try {
3331
+ this.emit(eventName, data);
3332
+ } catch {}
3333
+ }
3334
+ constructor(defaultSerial, options = false) {
3693
3335
  super(defaultSerial);
3336
+ const autoRefresh = typeof options === "boolean" ? options : options.autoRefresh ?? false;
3337
+ this.refreshIntervalMs = typeof options === "boolean" ? 200 : options.refreshIntervalMs ?? 200;
3694
3338
  this.autoRefresh = autoRefresh;
3695
3339
  }
3696
3340
  get autoRefresh() {
@@ -3706,6 +3350,21 @@ class V5SerialDevice extends VexSerialDevice {
3706
3350
  this._stopRefreshInterval();
3707
3351
  }
3708
3352
  }
3353
+ get refreshIntervalMs() {
3354
+ return this._refreshIntervalMs;
3355
+ }
3356
+ set refreshIntervalMs(value) {
3357
+ if (!Number.isFinite(value) || value <= 0) {
3358
+ throw new VexInvalidArgumentError("refreshIntervalMs must be a positive finite number");
3359
+ }
3360
+ if (this._refreshIntervalMs === value)
3361
+ return;
3362
+ this._refreshIntervalMs = value;
3363
+ if (this._refreshInterval !== undefined) {
3364
+ this._stopRefreshInterval();
3365
+ this._startRefreshInterval();
3366
+ }
3367
+ }
3709
3368
  _startRefreshInterval() {
3710
3369
  if (this._refreshInterval !== undefined || this._disposed)
3711
3370
  return;
@@ -3723,16 +3382,16 @@ class V5SerialDevice extends VexSerialDevice {
3723
3382
  try {
3724
3383
  const r = await this.refresh();
3725
3384
  if (r.isErr())
3726
- this.emit("error", r.error);
3385
+ this._emitSafely("error", r.error);
3727
3386
  } catch (error) {
3728
- this.emit("error", error);
3387
+ this._emitSafely("error", error);
3729
3388
  } finally {
3730
3389
  this._isLastRefreshComplete = true;
3731
3390
  }
3732
3391
  })();
3733
3392
  }
3734
3393
  }
3735
- }, 200);
3394
+ }, this._refreshIntervalMs);
3736
3395
  unrefTimerIfPossible(this._refreshInterval);
3737
3396
  }
3738
3397
  _stopRefreshInterval() {
@@ -3795,58 +3454,125 @@ class V5SerialDevice extends VexSerialDevice {
3795
3454
  })());
3796
3455
  }
3797
3456
  connect(conn) {
3798
- return new import_neverthrow5.ResultAsync(this._connect(conn));
3457
+ if (this._disposed) {
3458
+ return new import_neverthrow5.ResultAsync(Promise.resolve(this._staleLifecycleResult()));
3459
+ }
3460
+ if (this.isConnected)
3461
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.ok(undefined)));
3462
+ const generation = ++this._lifecycleGeneration;
3463
+ return new import_neverthrow5.ResultAsync(this._connect(conn, generation));
3799
3464
  }
3800
- async _connect(conn) {
3465
+ async _connect(conn, generation = this._lifecycleGeneration) {
3466
+ if (!this._isLifecycleCurrent(generation)) {
3467
+ return this._staleLifecycleResult();
3468
+ }
3801
3469
  if (this.isConnected)
3802
3470
  return import_neverthrow5.ok(undefined);
3803
3471
  if (conn != null) {
3804
3472
  if (!conn.isConnected) {
3805
3473
  const opened = await conn.open();
3474
+ if (!this._isLifecycleCurrent(generation)) {
3475
+ await conn.close();
3476
+ return this._staleLifecycleResult();
3477
+ }
3806
3478
  if (opened.isErr() || opened.value !== "opened") {
3807
3479
  return import_neverthrow5.err(new VexIoError("failed to open the supplied connection"));
3808
3480
  }
3809
3481
  }
3810
3482
  const q = await conn.query1();
3483
+ if (!this._isLifecycleCurrent(generation)) {
3484
+ await conn.close();
3485
+ return this._staleLifecycleResult();
3486
+ }
3811
3487
  if (q.isErr()) {
3812
3488
  await conn.close();
3813
3489
  return import_neverthrow5.err(q.error);
3814
3490
  }
3815
- this.connection = conn;
3491
+ if (!this._commitConnection(conn, generation)) {
3492
+ await conn.close();
3493
+ return this._staleLifecycleResult();
3494
+ }
3816
3495
  } else {
3817
3496
  let tryIdx = 0;
3497
+ let canRequestPort = true;
3498
+ const attemptedPorts = new Set;
3499
+ const attemptedPortNames = [];
3818
3500
  while (true) {
3819
- const c = new V5SerialConnection(this.defaultSerial);
3820
- const result = await c.open(tryIdx++, true);
3501
+ if (!this._isLifecycleCurrent(generation)) {
3502
+ return this._staleLifecycleResult();
3503
+ }
3504
+ const c = this.createConnection();
3505
+ let result = await c.open(tryIdx++, false);
3506
+ if (!this._isLifecycleCurrent(generation)) {
3507
+ await c.close();
3508
+ return this._staleLifecycleResult();
3509
+ }
3510
+ if (result.isOk() && result.value === "no-port" && canRequestPort) {
3511
+ canRequestPort = false;
3512
+ result = await c.open(tryIdx, true);
3513
+ if (!this._isLifecycleCurrent(generation)) {
3514
+ await c.close();
3515
+ return this._staleLifecycleResult();
3516
+ }
3517
+ }
3821
3518
  if (result.isErr()) {
3822
3519
  await c.close();
3823
3520
  return import_neverthrow5.err(result.error);
3824
3521
  }
3825
3522
  if (result.value === "no-port") {
3826
- return import_neverthrow5.err(new VexNotConnectedError("no V5 device was found"));
3523
+ const attempted = attemptedPortNames.length ? `; attempted ${attemptedPortNames.join(", ")}` : "";
3524
+ return import_neverthrow5.err(new VexNotConnectedError(`no responsive V5 device was found${attempted}`));
3827
3525
  }
3828
3526
  if (result.value === "busy") {
3829
3527
  await c.close();
3830
- continue;
3528
+ return import_neverthrow5.err(new VexNotConnectedError("the selected V5 serial port is busy"));
3529
+ }
3530
+ const port = c.port;
3531
+ if (port !== undefined && attemptedPorts.has(port)) {
3532
+ await c.close();
3533
+ const portName = describePort(port);
3534
+ return import_neverthrow5.err(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`));
3535
+ }
3536
+ if (port !== undefined) {
3537
+ attemptedPorts.add(port);
3538
+ const portName = describePort(port);
3539
+ if (portName !== undefined)
3540
+ attemptedPortNames.push(portName);
3831
3541
  }
3832
3542
  const q = await c.query1();
3543
+ if (!this._isLifecycleCurrent(generation)) {
3544
+ await c.close();
3545
+ return this._staleLifecycleResult();
3546
+ }
3833
3547
  if (q.isErr()) {
3834
3548
  await c.close();
3835
3549
  continue;
3836
3550
  }
3837
- this.connection = c;
3551
+ if (!this._commitConnection(c, generation)) {
3552
+ await c.close();
3553
+ return this._staleLifecycleResult();
3554
+ }
3838
3555
  break;
3839
3556
  }
3840
3557
  }
3841
- if (!this.isConnected)
3558
+ const connection = this.connection;
3559
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
3560
+ return this._staleLifecycleResult();
3561
+ }
3562
+ if (!connection.isConnected)
3842
3563
  return import_neverthrow5.err(new VexNotConnectedError);
3843
- await this.doAfterConnect();
3564
+ const initialized = await this.doAfterConnect(connection, generation);
3565
+ if (initialized.isErr())
3566
+ return initialized;
3844
3567
  return import_neverthrow5.ok(undefined);
3845
3568
  }
3846
3569
  async disconnect() {
3570
+ this._lifecycleGeneration++;
3571
+ this._refreshGeneration++;
3847
3572
  this._isDisconnecting = true;
3848
3573
  const connection = this.connection;
3849
3574
  this.connection = undefined;
3575
+ this._detachDisconnectListener();
3850
3576
  try {
3851
3577
  await connection?.close();
3852
3578
  } finally {
@@ -3860,9 +3586,21 @@ class V5SerialDevice extends VexSerialDevice {
3860
3586
  await this.disconnect();
3861
3587
  }
3862
3588
  reconnect(timeout = 0) {
3863
- return new import_neverthrow5.ResultAsync(this._reconnect(timeout));
3589
+ if (this._disposed) {
3590
+ return new import_neverthrow5.ResultAsync(Promise.resolve(this._staleLifecycleResult()));
3591
+ }
3592
+ if (timeout < 0) {
3593
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.err(new VexInvalidArgumentError("timeout must be non-negative"))));
3594
+ }
3595
+ if (this.isConnected)
3596
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.ok(undefined)));
3597
+ const generation = this._isReconnecting ? this._lifecycleGeneration : ++this._lifecycleGeneration;
3598
+ return new import_neverthrow5.ResultAsync(this._reconnect(timeout, generation));
3864
3599
  }
3865
- async _reconnect(timeout) {
3600
+ async _reconnect(timeout, generation = this._lifecycleGeneration) {
3601
+ if (!this._isLifecycleCurrent(generation)) {
3602
+ return this._staleLifecycleResult();
3603
+ }
3866
3604
  if (this.isConnected)
3867
3605
  return import_neverthrow5.ok(undefined);
3868
3606
  if (timeout < 0) {
@@ -3878,6 +3616,9 @@ class V5SerialDevice extends VexSerialDevice {
3878
3616
  return import_neverthrow5.err(new VexNotConnectedError);
3879
3617
  }
3880
3618
  }
3619
+ if (!this._isLifecycleCurrent(generation)) {
3620
+ return this._staleLifecycleResult();
3621
+ }
3881
3622
  if (this.isConnected)
3882
3623
  return import_neverthrow5.ok(undefined);
3883
3624
  }
@@ -3886,8 +3627,15 @@ class V5SerialDevice extends VexSerialDevice {
3886
3627
  while (timeout === 0 || Date.now() < endTime) {
3887
3628
  let tryIdx = 0;
3888
3629
  while (true) {
3889
- const c = new V5SerialConnection(this.defaultSerial);
3630
+ if (!this._isLifecycleCurrent(generation)) {
3631
+ return this._staleLifecycleResult();
3632
+ }
3633
+ const c = this.createConnection();
3890
3634
  const result = await c.open(tryIdx++, false);
3635
+ if (!this._isLifecycleCurrent(generation)) {
3636
+ await c.close();
3637
+ return this._staleLifecycleResult();
3638
+ }
3891
3639
  if (result.isErr()) {
3892
3640
  await c.close();
3893
3641
  return import_neverthrow5.err(result.error);
@@ -3899,6 +3647,10 @@ class V5SerialDevice extends VexSerialDevice {
3899
3647
  continue;
3900
3648
  }
3901
3649
  const status = await c.getSystemStatus(200);
3650
+ if (!this._isLifecycleCurrent(generation)) {
3651
+ await c.close();
3652
+ return this._staleLifecycleResult();
3653
+ }
3902
3654
  if (status.isErr()) {
3903
3655
  await c.close();
3904
3656
  continue;
@@ -3907,23 +3659,38 @@ class V5SerialDevice extends VexSerialDevice {
3907
3659
  await c.close();
3908
3660
  continue;
3909
3661
  }
3910
- this.connection = c;
3662
+ if (!this._commitConnection(c, generation)) {
3663
+ await c.close();
3664
+ return this._staleLifecycleResult();
3665
+ }
3911
3666
  break;
3912
3667
  }
3913
3668
  if (this.isConnected)
3914
3669
  break;
3915
3670
  const getPortCount = async () => (await this.defaultSerial.getPorts()).length;
3916
3671
  const portsCount = await getPortCount();
3672
+ if (!this._isLifecycleCurrent(generation)) {
3673
+ return this._staleLifecycleResult();
3674
+ }
3917
3675
  await sleepUntilAsync(async () => await getPortCount() !== portsCount, 1000);
3676
+ if (!this._isLifecycleCurrent(generation)) {
3677
+ return this._staleLifecycleResult();
3678
+ }
3918
3679
  }
3919
3680
  } catch (e) {
3920
3681
  return import_neverthrow5.err(toVexSerialError(e));
3921
3682
  } finally {
3922
3683
  this._isReconnecting = false;
3923
3684
  }
3924
- if (!this.isConnected)
3685
+ const connection = this.connection;
3686
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
3687
+ return this._staleLifecycleResult();
3688
+ }
3689
+ if (!connection.isConnected)
3925
3690
  return import_neverthrow5.err(new VexNotConnectedError);
3926
- await this.doAfterConnect();
3691
+ const initialized = await this.doAfterConnect(connection, generation);
3692
+ if (initialized.isErr())
3693
+ return initialized;
3927
3694
  return import_neverthrow5.ok(undefined);
3928
3695
  }
3929
3696
  async waitForReconnectToFinish() {
@@ -3933,18 +3700,69 @@ class V5SerialDevice extends VexSerialDevice {
3933
3700
  return;
3934
3701
  }
3935
3702
  }
3936
- async doAfterConnect() {
3937
- if (this.connection == null)
3938
- return;
3939
- this.connection.on("disconnected", (_data) => {
3940
- if (this._isDisconnecting)
3703
+ createConnection() {
3704
+ return new V5SerialConnection(this.defaultSerial);
3705
+ }
3706
+ _isLifecycleCurrent(generation) {
3707
+ return generation === this._lifecycleGeneration && !this._disposed;
3708
+ }
3709
+ _staleLifecycleResult() {
3710
+ return import_neverthrow5.err(new VexNotConnectedError("connection attempt was superseded"));
3711
+ }
3712
+ _commitConnection(connection, generation) {
3713
+ if (!this._isLifecycleCurrent(generation))
3714
+ return false;
3715
+ this._detachDisconnectListener();
3716
+ this.connection = connection;
3717
+ return true;
3718
+ }
3719
+ _detachDisconnectListener() {
3720
+ const subscription = this._disconnectListener;
3721
+ this._disconnectListener = undefined;
3722
+ subscription?.connection.remove("disconnected", subscription.listener);
3723
+ }
3724
+ async doAfterConnect(connection, generation) {
3725
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
3726
+ return this._staleLifecycleResult();
3727
+ }
3728
+ const listener = () => {
3729
+ if (this._isDisconnecting || !this._isLifecycleCurrent(generation) || this.connection !== connection) {
3941
3730
  return;
3942
- this.emit("disconnected", undefined);
3943
- if (this.autoReconnect) {
3944
- this.reconnect().mapErr((e) => this.emit("error", e));
3945
3731
  }
3946
- });
3947
- await this.refresh();
3732
+ this._lifecycleGeneration++;
3733
+ this._refreshGeneration++;
3734
+ this.connection = undefined;
3735
+ this._detachDisconnectListener();
3736
+ this._emitSafely("disconnected", undefined);
3737
+ if (this.autoReconnect && !this._disposed) {
3738
+ this.reconnect().mapErr((error) => this._emitSafely("error", error));
3739
+ }
3740
+ };
3741
+ connection.on("disconnected", listener);
3742
+ this._disconnectListener = { connection, listener };
3743
+ const refreshed = await this.refresh();
3744
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection || !connection.isConnected) {
3745
+ return this._staleLifecycleResult();
3746
+ }
3747
+ if (refreshed.isErr()) {
3748
+ await this._discardConnection(connection, generation);
3749
+ return import_neverthrow5.err(refreshed.error);
3750
+ }
3751
+ if (!refreshed.value) {
3752
+ await this._discardConnection(connection, generation);
3753
+ return import_neverthrow5.err(new VexNotConnectedError("initial device refresh did not produce a current snapshot"));
3754
+ }
3755
+ return import_neverthrow5.ok(undefined);
3756
+ }
3757
+ async _discardConnection(connection, generation) {
3758
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
3759
+ return;
3760
+ }
3761
+ this._lifecycleGeneration++;
3762
+ this._refreshGeneration++;
3763
+ this.connection = undefined;
3764
+ this._detachDisconnectListener();
3765
+ await connection.close();
3948
3766
  }
3949
3767
  refresh() {
3950
3768
  return new import_neverthrow5.ResultAsync(this._refresh());
@@ -3958,31 +3776,15 @@ class V5SerialDevice extends VexSerialDevice {
3958
3776
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
3959
3777
  return import_neverthrow5.ok(false);
3960
3778
  }
3961
- const ssPacket = await conn.getSystemStatus();
3962
- if (generation !== this._refreshGeneration || this._disposed)
3963
- return import_neverthrow5.ok(false);
3964
- if (ssPacket.isErr()) {
3965
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
3966
- return import_neverthrow5.ok(false);
3967
- }
3968
- const sfPacket = await conn.getSystemFlags();
3969
- if (generation !== this._refreshGeneration || this._disposed)
3970
- return import_neverthrow5.ok(false);
3971
- if (sfPacket.isErr()) {
3972
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
3973
- return import_neverthrow5.ok(false);
3974
- }
3975
- const rdPacket = await conn.getRadioStatus();
3976
- if (generation !== this._refreshGeneration || this._disposed)
3977
- return import_neverthrow5.ok(false);
3978
- if (rdPacket.isErr()) {
3979
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
3980
- return import_neverthrow5.ok(false);
3981
- }
3982
- const dsPacket = await conn.getDeviceStatus();
3779
+ const [ssPacket, sfPacket, rdPacket, dsPacket] = await Promise.all([
3780
+ conn.getSystemStatus(),
3781
+ conn.getSystemFlags(),
3782
+ conn.getRadioStatus(),
3783
+ conn.getDeviceStatus()
3784
+ ]);
3983
3785
  if (generation !== this._refreshGeneration || this._disposed)
3984
3786
  return import_neverthrow5.ok(false);
3985
- if (dsPacket.isErr()) {
3787
+ if (ssPacket.isErr() || sfPacket.isErr() || rdPacket.isErr() || dsPacket.isErr()) {
3986
3788
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
3987
3789
  return import_neverthrow5.ok(false);
3988
3790
  }
@@ -4039,7 +3841,7 @@ class V5SerialDevice extends VexSerialDevice {
4039
3841
  {
4040
3842
  battery: controller1Battery,
4041
3843
  isAvailable: controller1Available,
4042
- isCharging: (flags2 & 128) !== 0
3844
+ isCharging: undefined
4043
3845
  }
4044
3846
  ],
4045
3847
  radio: {
@@ -4066,7 +3868,26 @@ class V5SerialDevice extends VexSerialDevice {
4066
3868
  }
4067
3869
  this.state.matchMode = snapshot.matchMode;
4068
3870
  this.state.isFieldControllerConnected = snapshot.isFieldControllerConnected;
4069
- Object.assign(this.state.brain, snapshot.brain);
3871
+ const brain = this.state.brain;
3872
+ brain.activeProgram = snapshot.brain.activeProgram;
3873
+ brain.battery.batteryPercent = snapshot.brain.battery.batteryPercent;
3874
+ brain.battery.isCharging = snapshot.brain.battery.isCharging;
3875
+ brain.button.isPressed = snapshot.brain.button.isPressed;
3876
+ brain.button.isDoublePressed = snapshot.brain.button.isDoublePressed;
3877
+ if (brain.cpu0Version.compare(snapshot.brain.cpu0Version) !== 0) {
3878
+ brain.cpu0Version = snapshot.brain.cpu0Version;
3879
+ }
3880
+ if (brain.cpu1Version.compare(snapshot.brain.cpu1Version) !== 0) {
3881
+ brain.cpu1Version = snapshot.brain.cpu1Version;
3882
+ }
3883
+ brain.isAvailable = snapshot.brain.isAvailable;
3884
+ brain.settings.isScreenReversed = snapshot.brain.settings.isScreenReversed;
3885
+ brain.settings.isWhiteTheme = snapshot.brain.settings.isWhiteTheme;
3886
+ brain.settings.usingLanguage = snapshot.brain.settings.usingLanguage;
3887
+ if (brain.systemVersion.compare(snapshot.brain.systemVersion) !== 0) {
3888
+ brain.systemVersion = snapshot.brain.systemVersion;
3889
+ }
3890
+ brain.uniqueId = snapshot.brain.uniqueId;
4070
3891
  Object.assign(this.state.controllers[0], snapshot.controllers[0]);
4071
3892
  Object.assign(this.state.controllers[1], snapshot.controllers[1]);
4072
3893
  Object.assign(this.state.radio, snapshot.radio);
@@ -4075,10 +3896,18 @@ class V5SerialDevice extends VexSerialDevice {
4075
3896
  if (device != null)
4076
3897
  next[device.port] = device;
4077
3898
  }
4078
- this.state.devices = next;
3899
+ if (!sameSmartDeviceSlots(this.state.devices, next)) {
3900
+ this.state.devices = next;
3901
+ }
4079
3902
  return true;
4080
3903
  }
4081
3904
  }
3905
+ function sameSmartDeviceSlots(left, right) {
3906
+ return left.length === right.length && left.every((device, index) => {
3907
+ const next = right[index];
3908
+ 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;
3909
+ });
3910
+ }
4082
3911
  // src/VexIniConfig.ts
4083
3912
  class BaseIniBuilder {
4084
3913
  str = "";
@@ -4087,7 +3916,7 @@ class BaseIniBuilder {
4087
3916
  `;
4088
3917
  }
4089
3918
  addComment(comment) {
4090
- this.addLine("; " + comment);
3919
+ this.addLine(comment.length === 0 ? ";" : "; " + comment);
4091
3920
  return this;
4092
3921
  }
4093
3922
  getContent() {
@@ -4105,18 +3934,23 @@ class IniSectionBuilder extends BaseIniBuilder {
4105
3934
  this.object = object;
4106
3935
  this.keyTransform = keyTransform;
4107
3936
  }
4108
- addSingleObjProperty(key, maxValueLength) {
4109
- const value = this.object[key];
4110
- if (value == null || value.toString().length === 0)
3937
+ addSingleObjProperty(key, maxValueLength, omitIfEmpty = false) {
3938
+ const rawValue = this.object[key];
3939
+ if (rawValue == null)
3940
+ return;
3941
+ const value = rawValue.toString();
3942
+ if (omitIfEmpty && value.length === 0)
4111
3943
  return;
3944
+ if (/["\u0000-\u001f\u007f]/.test(value)) {
3945
+ throw new Error(`INI value for ${this.name}.${String(key)} contains an unsupported character`);
3946
+ }
4112
3947
  const formattedKey = this.keyTransform(key).padEnd(12).slice(0, 12);
4113
- const trimmedVal = value.toString().slice(0, maxValueLength);
4114
- const escapedVal = trimmedVal.replace(/["\\\u0000-\u001f\u007f]/g, (character) => `\\x${character.charCodeAt(0).toString(16).padStart(2, "0")}`);
4115
- this.addLine(`${formattedKey} = "${escapedVal}"`);
3948
+ const trimmedValue = value.slice(0, maxValueLength);
3949
+ this.addLine(`${formattedKey} = "${trimmedValue}"`);
4116
3950
  }
4117
- addObjProperty(key, maxValueLength) {
3951
+ addObjProperty(key, maxValueLength, omitIfEmpty = false) {
4118
3952
  for (const k of Array.isArray(key) ? key : [key]) {
4119
- this.addSingleObjProperty(k, maxValueLength);
3953
+ this.addSingleObjProperty(k, maxValueLength, omitIfEmpty);
4120
3954
  }
4121
3955
  return this;
4122
3956
  }
@@ -4134,6 +3968,11 @@ class IniFileBuilder extends BaseIniBuilder {
4134
3968
  this.str += section.getContent();
4135
3969
  return this;
4136
3970
  }
3971
+ addOptionalSection(section) {
3972
+ if (section.getContent().length === 0)
3973
+ return this;
3974
+ return this.addComment("").addSection(section);
3975
+ }
4137
3976
  }
4138
3977
 
4139
3978
  class ProgramIniConfig {
@@ -4164,12 +4003,12 @@ class ProgramIniConfig {
4164
4003
  if (this.program.date.length === 0) {
4165
4004
  this.setProgramDate(new Date);
4166
4005
  }
4167
- 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();
4006
+ 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();
4168
4007
  }
4169
4008
  dec2(value) {
4170
4009
  return (value % 100).toString(10).padStart(2, "0").toUpperCase();
4171
4010
  }
4172
4011
  }
4173
4012
 
4174
- //# debugId=6C653363AF816C0D64756E2164756E21
4013
+ //# debugId=60E9322CE95DA15264756E2164756E21
4175
4014
  //# sourceMappingURL=index.cjs.map