bmweb-cli 0.1.2 → 0.1.4
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/bmweb.js +75 -19
- package/package.json +1 -1
package/dist/bmweb.js
CHANGED
|
@@ -814,6 +814,7 @@ import { createInterface } from "node:readline";
|
|
|
814
814
|
|
|
815
815
|
// src/serial.ts
|
|
816
816
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
817
|
+
var RX_KICK_MS = 4;
|
|
817
818
|
var PORT_PATTERNS = [
|
|
818
819
|
/^cu\.usbserial/i,
|
|
819
820
|
/^cu\.SLAB/i,
|
|
@@ -827,8 +828,12 @@ var NodeSerialPort = class {
|
|
|
827
828
|
binding = null;
|
|
828
829
|
/** chunks heard and not yet read */
|
|
829
830
|
chunks = [];
|
|
830
|
-
/**
|
|
831
|
-
|
|
831
|
+
/** reads waiting for bytes, oldest first, when the queue is empty */
|
|
832
|
+
waiters = [];
|
|
833
|
+
/** the receive kick, running while a read waits (see startKick) */
|
|
834
|
+
kick = null;
|
|
835
|
+
/** a kick ioctl in flight, so they never pile up */
|
|
836
|
+
kicking = false;
|
|
832
837
|
/** the lines as last set, so a partial setSignals keeps the others */
|
|
833
838
|
lines = { dtr: false, rts: false, brk: false };
|
|
834
839
|
/** the wire trace sink, when the CLI wants one */
|
|
@@ -864,23 +869,25 @@ var NodeSerialPort = class {
|
|
|
864
869
|
async close() {
|
|
865
870
|
const b = this.binding;
|
|
866
871
|
this.binding = null;
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
this.waiter = null;
|
|
870
|
-
w({ value: void 0, done: true });
|
|
871
|
-
}
|
|
872
|
+
this.stopKick();
|
|
873
|
+
this.wakeAll();
|
|
872
874
|
this.chunks = [];
|
|
873
875
|
if (b) await b.close();
|
|
874
876
|
}
|
|
877
|
+
/** Tell every waiting read the port is done, and forget them. */
|
|
878
|
+
wakeAll() {
|
|
879
|
+
const ws = this.waiters;
|
|
880
|
+
this.waiters = [];
|
|
881
|
+
for (const w of ws) w({ value: void 0, done: true });
|
|
882
|
+
}
|
|
875
883
|
/**
|
|
876
884
|
* Bytes the binding heard: to the waiting read, else queued.
|
|
877
885
|
* @param chunk - the bytes
|
|
878
886
|
*/
|
|
879
887
|
push(chunk) {
|
|
880
888
|
if (!chunk.length) return;
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
this.waiter = null;
|
|
889
|
+
const w = this.waiters.shift();
|
|
890
|
+
if (w) {
|
|
884
891
|
w({ value: chunk, done: false });
|
|
885
892
|
return;
|
|
886
893
|
}
|
|
@@ -916,19 +923,54 @@ var NodeSerialPort = class {
|
|
|
916
923
|
if (next) return Promise.resolve({ value: next, done: false });
|
|
917
924
|
if (!this.binding) return Promise.resolve({ value: void 0, done: true });
|
|
918
925
|
return new Promise((resolve2) => {
|
|
919
|
-
this.
|
|
926
|
+
this.waiters.push(resolve2);
|
|
927
|
+
this.startKick();
|
|
920
928
|
});
|
|
921
929
|
}
|
|
930
|
+
/**
|
|
931
|
+
* Make the driver deliver what it has heard.
|
|
932
|
+
*
|
|
933
|
+
* THE BUG THIS FIXES. On macOS the built-in FTDI driver does not wake the
|
|
934
|
+
* reader when bytes arrive: with a read armed and the process simply
|
|
935
|
+
* waiting, an ECU's answer sat in the driver until some OTHER call touched
|
|
936
|
+
* the device (the next write, a modem-line change, close), and only then
|
|
937
|
+
* came out -- measured on a real car as 0 bytes for the first exchange
|
|
938
|
+
* after open and every later exchange delivering the PREVIOUS one's bytes
|
|
939
|
+
* at its start. Polling the modem lines (a TIOCMGET, no wire traffic)
|
|
940
|
+
* every few milliseconds while a read waits makes each answer arrive
|
|
941
|
+
* within the poll interval, first exchange included. The interval never
|
|
942
|
+
* holds the process open and stops itself once no read is waiting.
|
|
943
|
+
*/
|
|
944
|
+
startKick() {
|
|
945
|
+
if (this.kick) return;
|
|
946
|
+
const tick = () => {
|
|
947
|
+
const b = this.binding;
|
|
948
|
+
if (!b || !this.waiters.length) {
|
|
949
|
+
this.stopKick();
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
if (this.kicking) return;
|
|
953
|
+
this.kicking = true;
|
|
954
|
+
b.get().catch(() => null).then(() => {
|
|
955
|
+
this.kicking = false;
|
|
956
|
+
});
|
|
957
|
+
};
|
|
958
|
+
this.kick = setInterval(tick, RX_KICK_MS);
|
|
959
|
+
if (typeof this.kick === "object" && "unref" in this.kick)
|
|
960
|
+
this.kick.unref();
|
|
961
|
+
}
|
|
962
|
+
/** Stop the receive kick. */
|
|
963
|
+
stopKick() {
|
|
964
|
+
if (!this.kick) return;
|
|
965
|
+
clearInterval(this.kick);
|
|
966
|
+
this.kick = null;
|
|
967
|
+
}
|
|
922
968
|
/**
|
|
923
969
|
* Cancel the reader: a waiting read is told `done`, buffered bytes go.
|
|
924
970
|
*/
|
|
925
971
|
cancel() {
|
|
926
972
|
this.chunks = [];
|
|
927
|
-
|
|
928
|
-
const w = this.waiter;
|
|
929
|
-
this.waiter = null;
|
|
930
|
-
w({ value: void 0, done: true });
|
|
931
|
-
}
|
|
973
|
+
this.wakeAll();
|
|
932
974
|
}
|
|
933
975
|
/**
|
|
934
976
|
* Write bytes. Resolves when the OS has them, not when they have left the
|
|
@@ -1867,6 +1909,7 @@ function terminalHomeHost(api, status) {
|
|
|
1867
1909
|
var SHIFTED_DIGITS = "!@#$%^&*()";
|
|
1868
1910
|
var DIGITS = "1234567890";
|
|
1869
1911
|
var GAUGE_WIDTH = 12;
|
|
1912
|
+
var QUIT_MS = 4e3;
|
|
1870
1913
|
function keyToPress(k) {
|
|
1871
1914
|
if (k.ctrl && k.name === "c") return "quit";
|
|
1872
1915
|
if (k.name === "escape") return "back";
|
|
@@ -1941,6 +1984,7 @@ function nodeTerminal() {
|
|
|
1941
1984
|
});
|
|
1942
1985
|
} finally {
|
|
1943
1986
|
rl.close();
|
|
1987
|
+
input.resume();
|
|
1944
1988
|
raw(true);
|
|
1945
1989
|
paused = false;
|
|
1946
1990
|
}
|
|
@@ -2576,16 +2620,27 @@ async function tuiCommand(chassis, sgbd, opts = {}) {
|
|
|
2576
2620
|
if (opts.menu !== p.menu) await p.openMenu(opts.menu);
|
|
2577
2621
|
}
|
|
2578
2622
|
const program = p;
|
|
2623
|
+
let leaving = false;
|
|
2579
2624
|
const unsubscribe = term.onKey((k) => {
|
|
2580
2625
|
if (ui.modal) return;
|
|
2581
2626
|
const what = keyToPress(k);
|
|
2582
2627
|
if (what === null) return;
|
|
2583
2628
|
if (what === "quit") {
|
|
2584
|
-
|
|
2585
|
-
|
|
2629
|
+
if (leaving) {
|
|
2630
|
+
if (k.ctrl && !opts.term) {
|
|
2631
|
+
term.close();
|
|
2632
|
+
process.exit(130);
|
|
2633
|
+
}
|
|
2634
|
+
return;
|
|
2635
|
+
}
|
|
2636
|
+
leaving = true;
|
|
2637
|
+
const done = program.leaveModule().catch(() => {
|
|
2586
2638
|
});
|
|
2639
|
+
const late = new Promise((r2) => setTimeout(r2, QUIT_MS));
|
|
2640
|
+
Promise.race([done, late]).then(() => ui.left());
|
|
2587
2641
|
return;
|
|
2588
2642
|
}
|
|
2643
|
+
if (leaving) return;
|
|
2589
2644
|
if (what === "back") {
|
|
2590
2645
|
ui.requestStop();
|
|
2591
2646
|
program.back().catch(() => {
|
|
@@ -2607,7 +2662,7 @@ async function tuiCommand(chassis, sgbd, opts = {}) {
|
|
|
2607
2662
|
}
|
|
2608
2663
|
|
|
2609
2664
|
// src/bmweb.ts
|
|
2610
|
-
var VERSION = true ? "0.1.
|
|
2665
|
+
var VERSION = true ? "0.1.4" : "0.0.0-dev";
|
|
2611
2666
|
var INCLUDE = {
|
|
2612
2667
|
include: {
|
|
2613
2668
|
kind: "list",
|
|
@@ -2940,6 +2995,7 @@ async function main(argv, out = (l) => process.stdout.write(l + "\n"), err = (l)
|
|
|
2940
2995
|
if (typeof process !== "undefined" && process.argv[1] && /bmweb(\.js)?$/.test(process.argv[1])) {
|
|
2941
2996
|
main(process.argv.slice(2)).then((code) => {
|
|
2942
2997
|
process.exitCode = code;
|
|
2998
|
+
process.stdout.write("", () => process.exit(code));
|
|
2943
2999
|
});
|
|
2944
3000
|
}
|
|
2945
3001
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bmweb-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "BMWeb's tools as a command line: read INPA .IPO scripts, compile .IPS sources, search the corpus job index, decode and diff shared Garage reports, and, over a K+DCAN cable, run jobs, whole-car scans and INPA screens in the terminal.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|