bmweb-cli 0.1.0
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/LICENSE +674 -0
- package/README.md +351 -0
- package/dist/bmweb.js +2790 -0
- package/package.json +53 -0
- package/runtime/core/bestvm/codec.js +285 -0
- package/runtime/core/bestvm/environment.js +116 -0
- package/runtime/core/bestvm/executor.js +1483 -0
- package/runtime/core/bestvm/index.js +52 -0
- package/runtime/core/bestvm/machine.js +491 -0
- package/runtime/core/bestvm/operands.js +356 -0
- package/runtime/core/bestvm/registers.js +152 -0
- package/runtime/core/bestvm/write-guard.js +111 -0
- package/runtime/core/ipofile/compile.js +364 -0
- package/runtime/core/ipofile/decls.js +187 -0
- package/runtime/core/ipofile/emit.js +708 -0
- package/runtime/core/ipofile/exec.js +164 -0
- package/runtime/core/ipofile/lex.js +243 -0
- package/runtime/core/ipofile/parse.js +550 -0
- package/runtime/core/ipofile/pool.js +404 -0
- package/runtime/core/ipofile/walk.js +431 -0
- package/runtime/core/ipovm/builtin-helpers.js +182 -0
- package/runtime/core/ipovm/builtins-api.js +610 -0
- package/runtime/core/ipovm/builtins-screen.js +493 -0
- package/runtime/core/ipovm/builtins-table.js +166 -0
- package/runtime/core/ipovm/builtins-text.js +166 -0
- package/runtime/core/ipovm/emissions.js +138 -0
- package/runtime/core/ipovm/hosts.js +191 -0
- package/runtime/core/ipovm/operators.js +229 -0
- package/runtime/core/ipovm/structures.js +250 -0
- package/runtime/core/ipovm/suspensions.js +241 -0
- package/runtime/core/ipovm/tape.js +206 -0
- package/runtime/core/ipovm/values.js +241 -0
- package/runtime/core/ipovm/vm.js +1166 -0
- package/runtime/core/translate.js +526 -0
- package/runtime/core/webshim/api-router.js +592 -0
- package/runtime/core/webshim/bus.js +95 -0
- package/runtime/core/webshim/coding.js +82 -0
- package/runtime/core/webshim/data-fetch.js +66 -0
- package/runtime/core/webshim/exchange.js +288 -0
- package/runtime/core/webshim/framing.js +331 -0
- package/runtime/core/webshim/install.js +30 -0
- package/runtime/core/webshim/job-runner.js +319 -0
- package/runtime/core/webshim/native-bus.js +108 -0
- package/runtime/core/webshim/timers.js +82 -0
- package/runtime/core/webshim/trace.js +205 -0
- package/runtime/core/webshim/transport-base.js +128 -0
- package/runtime/core/webshim/variant-resolver.js +249 -0
- package/runtime/core/webshim/web-serial-bus.js +734 -0
- package/runtime/home/bmweb-home.ips +76 -0
- package/runtime/home/bmweb.h +26 -0
- package/runtime/screens/activations.js +258 -0
- package/runtime/screens/garage/diff.js +331 -0
- package/runtime/screens/garage/share.js +276 -0
- package/runtime/screens/garage/store.js +547 -0
- package/runtime/screens/ipo-runtime/cells.js +176 -0
- package/runtime/screens/ipo-runtime/dialogs.js +254 -0
- package/runtime/screens/ipo-runtime/home.js +358 -0
- package/runtime/screens/ipo-runtime/open.js +393 -0
- package/runtime/screens/ipo-runtime/paint-grid.js +106 -0
- package/runtime/screens/ipo-runtime/paint-modern.js +424 -0
- package/runtime/screens/ipo-runtime/print.js +281 -0
- package/runtime/screens/ipo-runtime/program.js +1337 -0
- package/runtime/screens/ipo-runtime/protocol.js +464 -0
- package/runtime/screens/ipo-runtime/script-scan.js +225 -0
- package/runtime/screens/ipo-runtime/translate-sets.js +130 -0
- package/runtime/screens/ipo-runtime/ui.js +249 -0
- package/runtime/screens/ipo-runtime/wire-policy.js +113 -0
- package/runtime/screens/ir.js +324 -0
- package/runtime/screens/search/data.js +153 -0
- package/runtime/screens/search/match.js +285 -0
- package/runtime/screens/search/open.js +66 -0
- package/runtime/vendor/fflate.min.js +1 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Builtins that convert between text and numbers: string length and
|
|
3
|
+
* slicing, int/real/string conversions, hex parsing, concatenation, and the
|
|
4
|
+
* date/time/API-string placeholders.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The first string-like argument parsed as a decimal, a comma accepted as
|
|
9
|
+
* the decimal point; 0 when unparseable.
|
|
10
|
+
* @param {IpoValue[]} stack - the call's arguments
|
|
11
|
+
* @returns {number}
|
|
12
|
+
*/
|
|
13
|
+
function parseDecimalArg(stack) {
|
|
14
|
+
const v = parseFloat(String(asStr(firstText(stack)) || '').replace(',', '.'));
|
|
15
|
+
return Number.isFinite(v) ? v : 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* strlen(->n, s): a bound value (Python's str subclass) counts, a slot does
|
|
20
|
+
* not. instr's Text argument is a concatenation (a bound value), so
|
|
21
|
+
* excluding it read length 0 and skipped the whole substring loop.
|
|
22
|
+
* @type {IpoBuiltin}
|
|
23
|
+
*/
|
|
24
|
+
function bStrlen(vm, stack) {
|
|
25
|
+
const x = firstText(stack);
|
|
26
|
+
storeOut(vm, stack, x == null ? 0 : asStr(x).length);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* midstr(->dst, src, start, len): a 0-based substring of the LAST
|
|
31
|
+
* string-like argument (bound values included), bound to the source's key.
|
|
32
|
+
* @type {IpoBuiltin}
|
|
33
|
+
*/
|
|
34
|
+
function bMidstr(vm, stack) {
|
|
35
|
+
const strs = stack.filter((x) => isPlainStr(x) || isBound(x)).map(asStr);
|
|
36
|
+
const ints = allInts(stack);
|
|
37
|
+
const src = strs.length ? strs[strs.length - 1] : '';
|
|
38
|
+
// midstr(dest, source, start, count): start is 0-based. E46.IPO's own
|
|
39
|
+
// instr() probes from 0 and takes midstr(text, 0, 2) for a mode prefix and
|
|
40
|
+
// midstr(text, 2, n) for the name after it; the coding screens chunk a hex
|
|
41
|
+
// string at 0, 2, 4, ... -- none of which works 1-based.
|
|
42
|
+
const a = ints.length ? ints[0] : 0;
|
|
43
|
+
const n = ints.length > 1 ? ints[1] : src.length;
|
|
44
|
+
storeOut(
|
|
45
|
+
vm,
|
|
46
|
+
stack,
|
|
47
|
+
src.slice(Math.max(0, a), Math.max(0, a) + Math.max(0, n)),
|
|
48
|
+
keyed(stack)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* inttostring / realtostring / formatnum(src, ->dst): the number's text,
|
|
54
|
+
* truncated to an integer. A BOUND VALUE COUNTS AS A STRING: a job result
|
|
55
|
+
* written through INPAapiResult* converts here, and matching plain strings
|
|
56
|
+
* only zeroed every converted result (S_ZUHEIZ printed "Startzähler : 0" for
|
|
57
|
+
* a counter the wire answered 7).
|
|
58
|
+
* @type {IpoBuiltin}
|
|
59
|
+
*/
|
|
60
|
+
function bInttostring(vm, stack) {
|
|
61
|
+
const n = firstNumberArg(stack);
|
|
62
|
+
storeOut(vm, stack, String(Math.trunc(n)), keyed(stack));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* inttolong / bytetoint: a NUMBER out, not its text. Mapped to inttostring
|
|
67
|
+
* they handed the fault printer "42" as a string, so `< 0` and longtoreal
|
|
68
|
+
* saw no number and every entry read "Nr: 0".
|
|
69
|
+
* @type {IpoBuiltin}
|
|
70
|
+
*/
|
|
71
|
+
function bIntwiden(vm, stack) {
|
|
72
|
+
const n = firstNumberArg(stack);
|
|
73
|
+
storeOut(vm, stack, Math.trunc(n), keyed(stack));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* stringtoreal(src, ->dst).
|
|
78
|
+
* @type {IpoBuiltin}
|
|
79
|
+
*/
|
|
80
|
+
function bStringtoreal(vm, stack) {
|
|
81
|
+
storeOut(vm, stack, parseDecimalArg(stack), keyed(stack));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* stringtoint(src, ->dst): the decimal truncated.
|
|
86
|
+
* @type {IpoBuiltin}
|
|
87
|
+
*/
|
|
88
|
+
function bStringtoint(vm, stack) {
|
|
89
|
+
storeOut(vm, stack, Math.trunc(parseDecimalArg(stack)), keyed(stack));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* hexconvert(src, ->b2, ->b1, ->b0, ->b3): a hex string split into bytes,
|
|
94
|
+
* in the order the scripts expect them.
|
|
95
|
+
* @type {IpoBuiltin}
|
|
96
|
+
*/
|
|
97
|
+
function bHexconvert(vm, stack) {
|
|
98
|
+
let v = parseInt(String(asStr(firstText(stack)) || '').trim() || '0', 16);
|
|
99
|
+
if (!Number.isFinite(v)) v = 0;
|
|
100
|
+
const refs = stack.filter(isRef);
|
|
101
|
+
const parts = [(v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff, (v >> 24) & 0xff];
|
|
102
|
+
refs.forEach((r, k) => {
|
|
103
|
+
if (k < 4) storeOut(vm, [r], parts[k]);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* strcat(->dst, a, b, ...): every string-like argument joined (dest ref
|
|
109
|
+
* FIRST -- not order-aligned with the other conversions).
|
|
110
|
+
* @type {IpoBuiltin}
|
|
111
|
+
*/
|
|
112
|
+
function bStrcat(vm, stack) {
|
|
113
|
+
const ins = stack.filter((x) => isPlainStr(x) || isBound(x)).map(asStr);
|
|
114
|
+
storeOut(vm, stack, ins.join(''), keyed(stack));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The inttoreal/realtoint/longtoreal family: a number out, unchanged. A job
|
|
119
|
+
* result or a converted value arrives BOUND (its text plus the key it came
|
|
120
|
+
* from): convert the text, as inttostring does.
|
|
121
|
+
* @type {IpoBuiltin}
|
|
122
|
+
*/
|
|
123
|
+
function bNumconvert(vm, stack) {
|
|
124
|
+
storeOut(vm, stack, firstNumberArg(stack), keyed(stack));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* getdate(->s): a fixed date offline.
|
|
129
|
+
* @type {IpoBuiltin}
|
|
130
|
+
*/
|
|
131
|
+
function bGetdate(vm, stack) {
|
|
132
|
+
storeOut(vm, stack, '01.01.2000');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* gettime(->s): a fixed time offline.
|
|
137
|
+
* @type {IpoBuiltin}
|
|
138
|
+
*/
|
|
139
|
+
function bGettime(vm, stack) {
|
|
140
|
+
storeOut(vm, stack, '00:00:00');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* getapistring(->s): empty.
|
|
145
|
+
* @type {IpoBuiltin}
|
|
146
|
+
*/
|
|
147
|
+
function bGetapistring(vm, stack) {
|
|
148
|
+
storeOut(vm, stack, '');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
152
|
+
module.exports = {
|
|
153
|
+
bStrlen,
|
|
154
|
+
bMidstr,
|
|
155
|
+
bInttostring,
|
|
156
|
+
bIntwiden,
|
|
157
|
+
bStringtoreal,
|
|
158
|
+
bStringtoint,
|
|
159
|
+
bHexconvert,
|
|
160
|
+
bStrcat,
|
|
161
|
+
bNumconvert,
|
|
162
|
+
bGetdate,
|
|
163
|
+
bGettime,
|
|
164
|
+
bGetapistring,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What a run of the .IPO VM produces: the drawn lines, the menu items,
|
|
3
|
+
* the jobs it sent, the dialogs it opened, and the navigation it performed.
|
|
4
|
+
* One Emissions object per run; the driver reads it after the run settles.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One drawn cell of a LINE. `t` says what drew it: a printed literal, a
|
|
9
|
+
* value bound to a result key, a lamp (digitalout) or a bar (analogout).
|
|
10
|
+
* @typedef {object} IpoElement
|
|
11
|
+
* @property {'text'|'value'|'lamp'|'gauge'} t - element kind
|
|
12
|
+
* @property {string} [s] - the text (a literal, or the live text of a value)
|
|
13
|
+
* @property {string} [key] - the result key a value/lamp/gauge is bound to
|
|
14
|
+
* @property {string[]} [also] - other keys folded into a concatenated value
|
|
15
|
+
* @property {Record<string, string>} [map] - a lookup table the value came from
|
|
16
|
+
* @property {string} [unit] - the *_EINH key drawn beside a value
|
|
17
|
+
* @property {number} [row] - screen row, relative to its LINE
|
|
18
|
+
* @property {number} [col] - screen column
|
|
19
|
+
* @property {number} [lrow] - the script's own row, once the driver rebases `row`
|
|
20
|
+
* @property {number} [min] - analogout scale minimum
|
|
21
|
+
* @property {number} [max] - analogout scale maximum
|
|
22
|
+
* @property {number} [warnLo] - analogout valid-band low edge
|
|
23
|
+
* @property {number} [warnHi] - analogout valid-band high edge
|
|
24
|
+
* @property {string} [fmt] - analogout display format ("6.2")
|
|
25
|
+
* @property {string} [on] - digitalout's TrueText
|
|
26
|
+
* @property {string} [off] - digitalout's FalseText
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* One LINE block's output: a logical line of INPA's virtual screen.
|
|
31
|
+
* @typedef {object} IpoLine
|
|
32
|
+
* @property {string|null} label - the LINE's name (what Select offers), or null
|
|
33
|
+
* @property {IpoElement[]} elements - the cells it drew
|
|
34
|
+
* @property {string} [jobArg] - the job argument the line was drawn for
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A menu ITEM as the run saw it: the F-key, its caption, and what pressing
|
|
39
|
+
* it was found to do.
|
|
40
|
+
* @typedef {object} IpoItem
|
|
41
|
+
* @property {number} nr - F-key number (11..20 = shifted)
|
|
42
|
+
* @property {string} [label] - caption
|
|
43
|
+
* @property {boolean} [fromSetitem] - declared by setitem() rather than an ITEM token
|
|
44
|
+
* @property {number|null} [on] - setitem's third argument: 1 shows the key, 0 hides it
|
|
45
|
+
* @property {string} [menu] - the menu it opens
|
|
46
|
+
* @property {string} [screen] - the screen it sets
|
|
47
|
+
* @property {string} [job] - the job it sends
|
|
48
|
+
* @property {string} [jobArg] - that job's argument
|
|
49
|
+
* @property {boolean} [stateJob] - the job was found inside a state machine
|
|
50
|
+
* @property {string} [stateEnter] - the state machine it starts
|
|
51
|
+
* @property {boolean} [faultRead] - it switches the fault-read mode
|
|
52
|
+
* @property {string[]} [prompt] - the input prompts it shows
|
|
53
|
+
* @property {string[]} [messages] - the message boxes it opens
|
|
54
|
+
* @property {string} [action] - select | deselect | exit | printscreen
|
|
55
|
+
* @property {boolean} [appTool] - scriptchange/callwin: an app-side tool
|
|
56
|
+
* @property {[number, IpoValue]} [_sel] - a record index it selects (`const v; store global`)
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A job the run sent.
|
|
61
|
+
* @typedef {object} IpoJobRecord
|
|
62
|
+
* @property {string} job - job name
|
|
63
|
+
* @property {string|null} sgbd - the SGBD the script addressed
|
|
64
|
+
* @property {string} [arg] - the argument, when not fed by an input dialog
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A message box the run opened.
|
|
69
|
+
* @typedef {object} IpoMessage
|
|
70
|
+
* @property {string} title - the box's title
|
|
71
|
+
* @property {string|null} body - its text
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
/** Everything one run of a proc emitted. */
|
|
75
|
+
class Emissions {
|
|
76
|
+
constructor() {
|
|
77
|
+
/** @type {string|null} settitle/setmenutitle */
|
|
78
|
+
this.title = null;
|
|
79
|
+
/** @type {IpoItem[]} */
|
|
80
|
+
this.items = [];
|
|
81
|
+
/** @type {IpoLine[]} */
|
|
82
|
+
this.lines = [];
|
|
83
|
+
/** @type {IpoJobRecord[]} */
|
|
84
|
+
this.jobs = [];
|
|
85
|
+
/** @type {string|null} the menu setmenu() handed control to */
|
|
86
|
+
this.menu = null;
|
|
87
|
+
/** @type {string|null} the screen setscreen() set */
|
|
88
|
+
this.screen = null;
|
|
89
|
+
/** @type {IpoMessage[]} */
|
|
90
|
+
this.messages = [];
|
|
91
|
+
/** @type {string[]} every builtin called, in order */
|
|
92
|
+
this.calls = [];
|
|
93
|
+
/** @type {string[]} the %STATE labels the run parked at */
|
|
94
|
+
this.states = [];
|
|
95
|
+
/** @type {string[]} result keys read (executed branches and harvested ones) */
|
|
96
|
+
this.reads = [];
|
|
97
|
+
/** @type {Set<string>} result keys a branch decided on */
|
|
98
|
+
this.predicateReads = new Set();
|
|
99
|
+
/** @type {string|null} the .IPO a scriptchange() handed control to */
|
|
100
|
+
this.scriptChange = null;
|
|
101
|
+
/**
|
|
102
|
+
* clearrect / ftextclear: screen areas the body blanked, in the
|
|
103
|
+
* script's own row / column coordinates; the runtime drops the cells
|
|
104
|
+
* it still holds there.
|
|
105
|
+
* @type {{row: number, col: number, h: number, w: number}[]}
|
|
106
|
+
*/
|
|
107
|
+
this.clears = [];
|
|
108
|
+
/**
|
|
109
|
+
* viewopen(file): the text file the script wrote and asked INPA to show
|
|
110
|
+
* (a whole-vehicle fault protocol), as its lines.
|
|
111
|
+
* @type {{path: string, lines: string[]}|null}
|
|
112
|
+
*/
|
|
113
|
+
this.view = null;
|
|
114
|
+
/**
|
|
115
|
+
* setscreen's second argument: TRUE = a frequent screen, re-run its cycle
|
|
116
|
+
* while it is current (INPA's WM_TIMER loop); null = no setscreen.
|
|
117
|
+
* @type {boolean|null}
|
|
118
|
+
*/
|
|
119
|
+
this.screenFrequent = null;
|
|
120
|
+
/** @type {boolean} blankscreen: the runtime clears the painted grid before the next cycle */
|
|
121
|
+
this.blank = false;
|
|
122
|
+
/** @type {boolean} exit: the script ended itself (INPA returns to script selection) */
|
|
123
|
+
this.exit = false;
|
|
124
|
+
/**
|
|
125
|
+
* setstate(&sm): the state machine a key handed control to. In wire mode
|
|
126
|
+
* the driver runs it (its picker, its job, its parks); offline the
|
|
127
|
+
* builtin executes it for the lift.
|
|
128
|
+
* @type {string|null}
|
|
129
|
+
*/
|
|
130
|
+
this.stateEnter = null;
|
|
131
|
+
/** @type {boolean} deselect(): the line filter Select set is cleared */
|
|
132
|
+
this.deselect = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
137
|
+
module.exports = { Emissions };
|
|
138
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Where the script's questions about the car get answered. The VM asks
|
|
3
|
+
* its host for job results, JOB_STATUS and the input-dialog state; the
|
|
4
|
+
* offline host answers with placeholders, the fed host serves what the wire
|
|
5
|
+
* returned. The VM does not know the difference.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What a job's answer looks like when fed to the VM: the flat union of every
|
|
10
|
+
* result set's keys, plus -- optionally -- the sets themselves, numbered as
|
|
11
|
+
* EDIABAS numbers them (0 = the system record, 1..n = what the job produced).
|
|
12
|
+
* @typedef {Map<string, string> & {sets?: Array<Record<string, string>>}} IpoFeed
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The host interface the VM calls.
|
|
17
|
+
* @typedef {object} IpoHost
|
|
18
|
+
* @property {(sgbd: string|null, job: string|null, arg: string|null, results: null) => object} job -
|
|
19
|
+
* run a job (offline hosts return nothing; the driven VM suspends instead)
|
|
20
|
+
* @property {(key: string, opts?: {integer?: boolean, set?: number, default?: *}) => (string|number)} result -
|
|
21
|
+
* INPAapiResult*: the value of a result key, numeric when asked as integer
|
|
22
|
+
* @property {() => string} status - INPAapiCheckJobStatus: the last job's JOB_STATUS
|
|
23
|
+
* @property {() => number} inputstate - getinputstate's placeholder
|
|
24
|
+
* @property {(m: IpoFeed|object) => void} [feed] - accept a job's results (FeedHost)
|
|
25
|
+
* @property {() => number} [count] - INPAapiResultSets: how many sets the job produced
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The OFFLINE host (mirrors ipo_vm.py's Host): jobs return nothing, reads read
|
|
30
|
+
* empty except the two invariants -- JOB_STATUS is OKAY, an integer read is 1
|
|
31
|
+
* (one of every list) -- which is enough to walk the tree and draw the
|
|
32
|
+
* maximal structure.
|
|
33
|
+
* @implements {IpoHost}
|
|
34
|
+
*/
|
|
35
|
+
class OkHost {
|
|
36
|
+
/**
|
|
37
|
+
* A job run offline produces nothing.
|
|
38
|
+
* @param {string|null} _sgbd - SGBD the script addressed
|
|
39
|
+
* @param {string|null} _job - job name
|
|
40
|
+
* @param {string|null} _arg - job argument
|
|
41
|
+
* @param {null} _results - unused
|
|
42
|
+
* @returns {object}
|
|
43
|
+
*/
|
|
44
|
+
job(_sgbd, _job, _arg, _results) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A placeholder read: JOB_STATUS, 1 for an integer, else the default or ''.
|
|
50
|
+
* @param {string} key - result key
|
|
51
|
+
* @param {{integer?: boolean, set?: number, default?: *}} [opts] - read options
|
|
52
|
+
* @returns {string|number}
|
|
53
|
+
*/
|
|
54
|
+
result(key, opts = {}) {
|
|
55
|
+
if (key === 'JOB_STATUS') return this.status();
|
|
56
|
+
if (opts.integer) return 1;
|
|
57
|
+
return opts.default != null ? opts.default : '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Offline every job succeeds.
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
status() {
|
|
65
|
+
return 'OKAY';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Offline every input is confirmed (the guard tests `== 0`).
|
|
70
|
+
* @returns {number}
|
|
71
|
+
*/
|
|
72
|
+
inputstate() {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A host the driver FEEDS: resume(resultsMap) lands here, and the machine's
|
|
79
|
+
* INPAapiResult* reads serve from the most recent job's results -- including
|
|
80
|
+
* JOB_STATUS, so INPAapiCheckJobStatus sees the wire's own verdict.
|
|
81
|
+
* @implements {IpoHost}
|
|
82
|
+
*/
|
|
83
|
+
class FeedHost {
|
|
84
|
+
constructor() {
|
|
85
|
+
/** @type {Map<string, string>} the flat union of the last job's keys */
|
|
86
|
+
this.map = new Map();
|
|
87
|
+
/**
|
|
88
|
+
* EDIABAS numbers a job's result sets 0..n: set 0 is the system record
|
|
89
|
+
* (OBJECT, VARIANTE, JOBNAME, SAETZE), sets 1..n are what the job
|
|
90
|
+
* produced -- one per fault for FS_LESEN, the last one carrying
|
|
91
|
+
* JOB_STATUS. A feed may carry that array as `sets`; the flat map is the
|
|
92
|
+
* union of every set and answers a read that names no set.
|
|
93
|
+
* @type {Array<Record<string, string>>|null}
|
|
94
|
+
*/
|
|
95
|
+
this.sets = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Take a job's results as the current answer.
|
|
100
|
+
* @param {IpoFeed|object} m - a Map (with an optional `sets` array) or a plain object
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
feed(m) {
|
|
104
|
+
this.map = m instanceof Map ? m : new Map(Object.entries(m || {}));
|
|
105
|
+
this.sets = m && Array.isArray(m.sets) ? m.sets : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The driven VM never runs a job through the host: it suspends and the
|
|
110
|
+
* driver feeds the answer back.
|
|
111
|
+
* @param {string|null} _sgbd - SGBD the script addressed
|
|
112
|
+
* @param {string|null} _job - job name
|
|
113
|
+
* @param {string|null} _arg - job argument
|
|
114
|
+
* @param {null} _results - unused
|
|
115
|
+
* @returns {object}
|
|
116
|
+
*/
|
|
117
|
+
job(_sgbd, _job, _arg, _results) {
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* INPAapiResultSets: how many sets the job produced (n, without set 0).
|
|
123
|
+
* @returns {number}
|
|
124
|
+
*/
|
|
125
|
+
count() {
|
|
126
|
+
return this.sets ? Math.max(0, this.sets.length - 1) : 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A read served from the numbered set when one is named and carried, else
|
|
131
|
+
* from the flat map. JOB_STATUS is asked of the last set; the wire's
|
|
132
|
+
* verdict answers wherever the shim put it.
|
|
133
|
+
* @param {string} key - result key
|
|
134
|
+
* @param {{integer?: boolean, set?: number, default?: *}} [opts] - read options
|
|
135
|
+
* @returns {string|number}
|
|
136
|
+
*/
|
|
137
|
+
/**
|
|
138
|
+
* The value behind a key as the wire delivered it (a binary result is
|
|
139
|
+
* bytes, not text): from the numbered set when one is named, else the
|
|
140
|
+
* flat map.
|
|
141
|
+
* @param {string} key - the result name
|
|
142
|
+
* @param {{set?: number}} [opts]
|
|
143
|
+
* @returns {*}
|
|
144
|
+
*/
|
|
145
|
+
raw(key, opts = {}) {
|
|
146
|
+
const si = opts.set;
|
|
147
|
+
if (this.sets && si != null && si >= 0 && si < this.sets.length) {
|
|
148
|
+
const s = this.sets[si] || {};
|
|
149
|
+
return Object.prototype.hasOwnProperty.call(s, key) ? s[key] : undefined;
|
|
150
|
+
}
|
|
151
|
+
return this.map.get(key);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
result(key, opts = {}) {
|
|
155
|
+
let v;
|
|
156
|
+
const si = opts.set;
|
|
157
|
+
if (this.sets && si != null && si >= 0 && si < this.sets.length) {
|
|
158
|
+
const s = this.sets[si] || {};
|
|
159
|
+
v = Object.prototype.hasOwnProperty.call(s, key) ? s[key] : undefined;
|
|
160
|
+
if (v == null && key === 'JOB_STATUS') return this.status();
|
|
161
|
+
} else {
|
|
162
|
+
if (key === 'JOB_STATUS') return this.status();
|
|
163
|
+
v = this.map.get(key);
|
|
164
|
+
}
|
|
165
|
+
if (v == null) {
|
|
166
|
+
return opts.integer ? 0 : opts.default != null ? opts.default : '';
|
|
167
|
+
}
|
|
168
|
+
return opts.integer ? parseInt(v, 10) || 0 : String(v);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The fed JOB_STATUS, OKAY when the feed carried none.
|
|
173
|
+
* @returns {string}
|
|
174
|
+
*/
|
|
175
|
+
status() {
|
|
176
|
+
const st = this.map.get('JOB_STATUS');
|
|
177
|
+
return st != null ? String(st) : 'OKAY';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* getinputstate's placeholder when nothing was driven.
|
|
182
|
+
* @returns {number}
|
|
183
|
+
*/
|
|
184
|
+
inputstate() {
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
190
|
+
module.exports = { OkHost, FeedHost };
|
|
191
|
+
}
|