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.
Files changed (72) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +351 -0
  3. package/dist/bmweb.js +2790 -0
  4. package/package.json +53 -0
  5. package/runtime/core/bestvm/codec.js +285 -0
  6. package/runtime/core/bestvm/environment.js +116 -0
  7. package/runtime/core/bestvm/executor.js +1483 -0
  8. package/runtime/core/bestvm/index.js +52 -0
  9. package/runtime/core/bestvm/machine.js +491 -0
  10. package/runtime/core/bestvm/operands.js +356 -0
  11. package/runtime/core/bestvm/registers.js +152 -0
  12. package/runtime/core/bestvm/write-guard.js +111 -0
  13. package/runtime/core/ipofile/compile.js +364 -0
  14. package/runtime/core/ipofile/decls.js +187 -0
  15. package/runtime/core/ipofile/emit.js +708 -0
  16. package/runtime/core/ipofile/exec.js +164 -0
  17. package/runtime/core/ipofile/lex.js +243 -0
  18. package/runtime/core/ipofile/parse.js +550 -0
  19. package/runtime/core/ipofile/pool.js +404 -0
  20. package/runtime/core/ipofile/walk.js +431 -0
  21. package/runtime/core/ipovm/builtin-helpers.js +182 -0
  22. package/runtime/core/ipovm/builtins-api.js +610 -0
  23. package/runtime/core/ipovm/builtins-screen.js +493 -0
  24. package/runtime/core/ipovm/builtins-table.js +166 -0
  25. package/runtime/core/ipovm/builtins-text.js +166 -0
  26. package/runtime/core/ipovm/emissions.js +138 -0
  27. package/runtime/core/ipovm/hosts.js +191 -0
  28. package/runtime/core/ipovm/operators.js +229 -0
  29. package/runtime/core/ipovm/structures.js +250 -0
  30. package/runtime/core/ipovm/suspensions.js +241 -0
  31. package/runtime/core/ipovm/tape.js +206 -0
  32. package/runtime/core/ipovm/values.js +241 -0
  33. package/runtime/core/ipovm/vm.js +1166 -0
  34. package/runtime/core/translate.js +526 -0
  35. package/runtime/core/webshim/api-router.js +592 -0
  36. package/runtime/core/webshim/bus.js +95 -0
  37. package/runtime/core/webshim/coding.js +82 -0
  38. package/runtime/core/webshim/data-fetch.js +66 -0
  39. package/runtime/core/webshim/exchange.js +288 -0
  40. package/runtime/core/webshim/framing.js +331 -0
  41. package/runtime/core/webshim/install.js +30 -0
  42. package/runtime/core/webshim/job-runner.js +319 -0
  43. package/runtime/core/webshim/native-bus.js +108 -0
  44. package/runtime/core/webshim/timers.js +82 -0
  45. package/runtime/core/webshim/trace.js +205 -0
  46. package/runtime/core/webshim/transport-base.js +128 -0
  47. package/runtime/core/webshim/variant-resolver.js +249 -0
  48. package/runtime/core/webshim/web-serial-bus.js +734 -0
  49. package/runtime/home/bmweb-home.ips +76 -0
  50. package/runtime/home/bmweb.h +26 -0
  51. package/runtime/screens/activations.js +258 -0
  52. package/runtime/screens/garage/diff.js +331 -0
  53. package/runtime/screens/garage/share.js +276 -0
  54. package/runtime/screens/garage/store.js +547 -0
  55. package/runtime/screens/ipo-runtime/cells.js +176 -0
  56. package/runtime/screens/ipo-runtime/dialogs.js +254 -0
  57. package/runtime/screens/ipo-runtime/home.js +358 -0
  58. package/runtime/screens/ipo-runtime/open.js +393 -0
  59. package/runtime/screens/ipo-runtime/paint-grid.js +106 -0
  60. package/runtime/screens/ipo-runtime/paint-modern.js +424 -0
  61. package/runtime/screens/ipo-runtime/print.js +281 -0
  62. package/runtime/screens/ipo-runtime/program.js +1337 -0
  63. package/runtime/screens/ipo-runtime/protocol.js +464 -0
  64. package/runtime/screens/ipo-runtime/script-scan.js +225 -0
  65. package/runtime/screens/ipo-runtime/translate-sets.js +130 -0
  66. package/runtime/screens/ipo-runtime/ui.js +249 -0
  67. package/runtime/screens/ipo-runtime/wire-policy.js +113 -0
  68. package/runtime/screens/ir.js +324 -0
  69. package/runtime/screens/search/data.js +153 -0
  70. package/runtime/screens/search/match.js +285 -0
  71. package/runtime/screens/search/open.js +66 -0
  72. 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
+ }