@tsplc/debug 0.2.17 → 0.2.18
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/format.js +36 -18
- package/dist/format.js.map +4 -4
- package/dist/index.js +768 -516
- package/dist/index.js.map +20 -17
- package/dist/manifest.js +6 -2
- package/dist/manifest.js.map +3 -3
- package/dist/paths.js +60 -28
- package/dist/paths.js.map +3 -3
- package/package.json +47 -46
- package/src/adapter.ts +43 -36
- package/src/api.ts +126 -123
- package/src/debugger.ts +584 -467
- package/src/errors.ts +3 -0
- package/src/hardware.ts +127 -96
- package/src/http.ts +147 -116
- package/src/index.ts +34 -4
- package/src/jobs.ts +102 -77
- package/src/login-value.ts +165 -0
- package/src/login.ts +233 -253
- package/src/manifest.ts +200 -194
- package/src/opcua.ts +55 -55
- package/src/paths.ts +230 -157
- package/src/requests.ts +86 -0
- package/src/telemetry.ts +104 -90
- package/src/timeline.ts +96 -61
- package/src/trace.ts +41 -31
- package/src/ui/bits.ts +45 -29
- package/src/ui/client.ts +33 -26
- package/src/ui/dom.ts +189 -108
- package/src/ui/format.ts +127 -72
- package/src/ui/hardware.ts +416 -343
- package/src/ui/header.ts +186 -164
- package/src/ui/index.ts +16 -9
- package/src/ui/jobs.ts +263 -214
- package/src/ui/main.ts +52 -48
- package/src/ui/overview.ts +152 -108
- package/src/ui/recent.ts +329 -276
- package/src/ui/requests.ts +69 -73
- package/src/ui/store.ts +236 -198
- package/src/ui/tree.ts +162 -130
- package/src/ui/variables.ts +557 -431
- package/src/watch.ts +22 -17
- package/src/ui/dev.ts +0 -579
- package/src/ui/fixture.ts +0 -548
package/dist/index.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
// packages/debug/src/debugger.ts
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { requestsReply } from "@tsplc/server/requests";
|
|
5
|
+
import { telemetryReply } from "@tsplc/server/telemetry";
|
|
6
|
+
|
|
7
|
+
// packages/debug/src/errors.ts
|
|
8
|
+
var errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
6
9
|
|
|
7
10
|
// packages/debug/src/adapter.ts
|
|
8
|
-
var qualityRank = {
|
|
11
|
+
var qualityRank = {
|
|
12
|
+
good: 0,
|
|
13
|
+
uncertain: 1,
|
|
14
|
+
bad: 2,
|
|
15
|
+
unavailable: 3
|
|
16
|
+
};
|
|
9
17
|
var worstQuality = (samples) => samples.reduce((worst, sample) => {
|
|
10
18
|
const quality = sample?.quality ?? "unavailable";
|
|
11
19
|
return qualityRank[quality] > qualityRank[worst] ? quality : worst;
|
|
@@ -32,16 +40,31 @@ var deviceReads = (device, io, typeOf) => [
|
|
|
32
40
|
...device.assemblies.flatMap(({ raw, bytes }) => raw ? [
|
|
33
41
|
{
|
|
34
42
|
target: raw,
|
|
35
|
-
type: {
|
|
43
|
+
type: {
|
|
44
|
+
kind: "array",
|
|
45
|
+
element: "BYTE",
|
|
46
|
+
length: bytes,
|
|
47
|
+
lower: 0
|
|
48
|
+
}
|
|
36
49
|
}
|
|
37
50
|
] : []),
|
|
38
51
|
...io.filter((binding) => binding.device === device.id).map(({ target, type }) => ({ target, type: typeOf(type) })),
|
|
39
|
-
...Object.values(device.status ?? {}).map((target) => ({
|
|
52
|
+
...Object.values(device.status ?? {}).map((target) => ({
|
|
53
|
+
target,
|
|
54
|
+
type: untyped
|
|
55
|
+
}))
|
|
40
56
|
];
|
|
57
|
+
var healthState = (quality, faulted) => {
|
|
58
|
+
if (quality !== "good") {
|
|
59
|
+
return "stale";
|
|
60
|
+
}
|
|
61
|
+
return faulted ? "fault" : "ok";
|
|
62
|
+
};
|
|
41
63
|
var health = (device, value) => {
|
|
42
64
|
const { state, states, diagnostic, message } = device.health ?? {};
|
|
43
|
-
if (!state && !diagnostic)
|
|
65
|
+
if (!state && !diagnostic) {
|
|
44
66
|
return { state: "unknown", quality: "unavailable" };
|
|
67
|
+
}
|
|
45
68
|
const stateSample = state ? value(state) : undefined;
|
|
46
69
|
const diagnosticSample = diagnostic ? value(diagnostic) : undefined;
|
|
47
70
|
const messageSample = message ? value(message) : undefined;
|
|
@@ -53,11 +76,15 @@ var health = (device, value) => {
|
|
|
53
76
|
const name = Object.entries(states ?? {}).find(([, member]) => member === number)?.[0] ?? (typeof stateSample?.value === "string" ? stateSample.value : undefined);
|
|
54
77
|
const faulted = diagnosticSample?.value === true || name !== undefined && faultyState.test(name);
|
|
55
78
|
return {
|
|
56
|
-
state: quality
|
|
79
|
+
state: healthState(quality, faulted),
|
|
57
80
|
...number !== undefined && { value: number },
|
|
58
81
|
...name && { name },
|
|
59
|
-
...typeof diagnosticSample?.value === "boolean" && {
|
|
60
|
-
|
|
82
|
+
...typeof diagnosticSample?.value === "boolean" && {
|
|
83
|
+
diagnostic: diagnosticSample.value
|
|
84
|
+
},
|
|
85
|
+
...typeof messageSample?.value === "string" && {
|
|
86
|
+
message: messageSample.value
|
|
87
|
+
},
|
|
61
88
|
quality
|
|
62
89
|
};
|
|
63
90
|
};
|
|
@@ -83,7 +110,10 @@ var deviceState = (device, io, value) => ({
|
|
|
83
110
|
] : []),
|
|
84
111
|
status: Object.fromEntries(Object.entries(device.status ?? {}).map(([label, target]) => [
|
|
85
112
|
label,
|
|
86
|
-
{
|
|
113
|
+
{
|
|
114
|
+
value: value(target)?.value ?? null,
|
|
115
|
+
quality: value(target)?.quality ?? "unavailable"
|
|
116
|
+
}
|
|
87
117
|
]))
|
|
88
118
|
});
|
|
89
119
|
|
|
@@ -97,7 +127,10 @@ var detailReads = (job) => [
|
|
|
97
127
|
{ target: `${job.target}.step`, type: scalar("UINT") },
|
|
98
128
|
{ target: `${job.target}.cancelling`, type: scalar("BOOL") },
|
|
99
129
|
...job.startedAt ? [{ target: job.startedAt, type: scalar("UDINT") }] : [],
|
|
100
|
-
...job.requests.map(({ sequence }) => ({
|
|
130
|
+
...job.requests.map(({ sequence }) => ({
|
|
131
|
+
target: sequence,
|
|
132
|
+
type: scalar("UDINT")
|
|
133
|
+
}))
|
|
101
134
|
];
|
|
102
135
|
var busyOf = (job, value, traced) => {
|
|
103
136
|
const read = value(busyRead(job).target);
|
|
@@ -116,15 +149,21 @@ var jobState = (job, inputs) => {
|
|
|
116
149
|
...startedAt && { startedAt },
|
|
117
150
|
...inputs.failure && { failure: inputs.failure }
|
|
118
151
|
};
|
|
119
|
-
if (!busy)
|
|
152
|
+
if (!busy) {
|
|
120
153
|
return base;
|
|
121
|
-
|
|
154
|
+
}
|
|
155
|
+
if (inputs.unread) {
|
|
122
156
|
return { ...base, unread: true };
|
|
157
|
+
}
|
|
123
158
|
const step = numberOf(value(`${job.target}.step`));
|
|
124
159
|
const current = job.steps.find((candidate) => candidate.step === step);
|
|
125
160
|
const calls = job.requests.map((call) => {
|
|
126
161
|
const sequence = numberOf(value(call.sequence)) ?? 0;
|
|
127
|
-
const trace = inputs.request({
|
|
162
|
+
const trace = inputs.request({
|
|
163
|
+
job: call.job ?? job.id,
|
|
164
|
+
service: call.service,
|
|
165
|
+
sequence
|
|
166
|
+
});
|
|
128
167
|
return { service: call.service, sequence, trace };
|
|
129
168
|
}).filter(({ sequence }) => sequence > 0);
|
|
130
169
|
const later = (call, than) => call.trace && than.trace ? call.trace.startedAt > than.trace.startedAt : call.sequence > than.sequence;
|
|
@@ -132,9 +171,13 @@ var jobState = (job, inputs) => {
|
|
|
132
171
|
const cancelling = value(`${job.target}.cancelling`);
|
|
133
172
|
return {
|
|
134
173
|
...base,
|
|
135
|
-
...cancelling?.quality === "good" && {
|
|
174
|
+
...cancelling?.quality === "good" && {
|
|
175
|
+
cancelling: cancelling.value === true
|
|
176
|
+
},
|
|
136
177
|
...step !== undefined && { step },
|
|
137
|
-
...current && {
|
|
178
|
+
...current && {
|
|
179
|
+
current: { kind: current.kind, range: current.range, text: current.text }
|
|
180
|
+
},
|
|
138
181
|
...startedAt && { elapsedMs: Math.max(0, now - Date.parse(startedAt)) },
|
|
139
182
|
...latest && {
|
|
140
183
|
request: {
|
|
@@ -147,15 +190,22 @@ var jobState = (job, inputs) => {
|
|
|
147
190
|
};
|
|
148
191
|
|
|
149
192
|
// packages/debug/src/paths.ts
|
|
150
|
-
|
|
151
|
-
var
|
|
193
|
+
import { isArrayIndexSegment, symbolPathSegments } from "@tsplc/opcua/contract";
|
|
194
|
+
var anyIndex = "[*]";
|
|
195
|
+
var pathSegments = (path) => path.split(anyIndex).flatMap((part, index) => [
|
|
196
|
+
...index > 0 ? [anyIndex] : [],
|
|
197
|
+
...symbolPathSegments(part)
|
|
198
|
+
]);
|
|
152
199
|
var joinPath = (prefix, segment) => segment.startsWith("[") || !prefix ? `${prefix}${segment}` : `${prefix}.${segment}`;
|
|
153
200
|
var typeOf = (manifest, id) => manifest.types[id] ?? { kind: "scalar", iec: id };
|
|
154
201
|
var createPaths = (manifest) => {
|
|
155
202
|
const typeOfId = (id) => typeOf(manifest, id);
|
|
156
203
|
const bases = new Map([
|
|
157
204
|
...manifest.roots.map(({ path, ...root }) => [path, { kind: "target", path, ...root }]),
|
|
158
|
-
...manifest.io.map(({ path, target, type }) => [
|
|
205
|
+
...manifest.io.map(({ path, target, type }) => [
|
|
206
|
+
path,
|
|
207
|
+
{ kind: "target", path, target, type, readable: true }
|
|
208
|
+
])
|
|
159
209
|
]);
|
|
160
210
|
const constants = new Map(manifest.constants.map(({ path, ...constant }) => [
|
|
161
211
|
path,
|
|
@@ -166,28 +216,32 @@ var createPaths = (manifest) => {
|
|
|
166
216
|
let prefix = "";
|
|
167
217
|
for (const segment of pathSegments(path)) {
|
|
168
218
|
let children = groups.get(prefix);
|
|
169
|
-
if (!children)
|
|
219
|
+
if (!children) {
|
|
170
220
|
groups.set(prefix, children = new Set);
|
|
221
|
+
}
|
|
171
222
|
children.add(segment);
|
|
172
223
|
prefix = joinPath(prefix, segment);
|
|
173
224
|
}
|
|
174
225
|
}
|
|
175
226
|
const resolve = (path) => {
|
|
176
227
|
const constant = constants.get(path);
|
|
177
|
-
if (constant)
|
|
228
|
+
if (constant) {
|
|
178
229
|
return constant;
|
|
230
|
+
}
|
|
179
231
|
const segments = pathSegments(path);
|
|
180
232
|
for (let length = segments.length;length > 0; length--) {
|
|
181
233
|
const base = bases.get(segments.slice(0, length).reduce(joinPath, ""));
|
|
182
|
-
if (!base)
|
|
234
|
+
if (!base) {
|
|
183
235
|
continue;
|
|
236
|
+
}
|
|
184
237
|
let resolved = base;
|
|
185
238
|
for (const segment of segments.slice(length)) {
|
|
186
239
|
const type = typeOfId(resolved.type);
|
|
187
240
|
if (type.kind === "record") {
|
|
188
241
|
const field = type.fields.find(({ name }) => name === segment);
|
|
189
|
-
if (!field)
|
|
242
|
+
if (!field) {
|
|
190
243
|
return;
|
|
244
|
+
}
|
|
191
245
|
resolved = {
|
|
192
246
|
kind: "target",
|
|
193
247
|
path: joinPath(resolved.path, segment),
|
|
@@ -198,32 +252,42 @@ var createPaths = (manifest) => {
|
|
|
198
252
|
...field.declaration && { declaration: field.declaration }
|
|
199
253
|
};
|
|
200
254
|
} else if (type.kind === "array") {
|
|
201
|
-
|
|
202
|
-
if (index === undefined || Number(index) < type.lower || Number(index) >= type.lower + type.length)
|
|
255
|
+
if (!isArrayIndexSegment(segment)) {
|
|
203
256
|
return;
|
|
257
|
+
}
|
|
258
|
+
const index = Number(segment.slice(1, -1));
|
|
259
|
+
if (index < type.lower || index >= type.lower + type.length) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
204
262
|
resolved = {
|
|
205
263
|
kind: "target",
|
|
206
264
|
path: joinPath(resolved.path, segment),
|
|
207
|
-
target: `${resolved.target}
|
|
265
|
+
target: `${resolved.target}${segment}`,
|
|
208
266
|
type: type.element,
|
|
209
267
|
readable: resolved.readable
|
|
210
268
|
};
|
|
211
|
-
} else
|
|
269
|
+
} else {
|
|
212
270
|
return;
|
|
271
|
+
}
|
|
213
272
|
}
|
|
214
273
|
return resolved;
|
|
215
274
|
}
|
|
216
275
|
};
|
|
217
276
|
const typedChildren = (resolved) => {
|
|
218
277
|
const type = resolved?.kind === "target" ? typeOfId(resolved.type) : undefined;
|
|
219
|
-
if (type?.kind === "record")
|
|
278
|
+
if (type?.kind === "record") {
|
|
220
279
|
return type.fields.map(({ name }) => name);
|
|
221
|
-
|
|
280
|
+
}
|
|
281
|
+
if (type?.kind === "array") {
|
|
222
282
|
return Array.from({ length: type.length }, (_, index) => `[${type.lower + index}]`);
|
|
283
|
+
}
|
|
223
284
|
return [];
|
|
224
285
|
};
|
|
225
286
|
const children = (path) => {
|
|
226
|
-
const names = [
|
|
287
|
+
const names = [
|
|
288
|
+
...typedChildren(resolve(path)),
|
|
289
|
+
...groups.get(path) ?? []
|
|
290
|
+
];
|
|
227
291
|
return [...new Set(names)].map((name) => {
|
|
228
292
|
const child = joinPath(path, name);
|
|
229
293
|
return { name, ...resolve(child) ?? { kind: "group", path: child } };
|
|
@@ -234,48 +298,184 @@ var createPaths = (manifest) => {
|
|
|
234
298
|
const leaves = new Set;
|
|
235
299
|
const add = (path) => {
|
|
236
300
|
leaves.add(path);
|
|
237
|
-
if (leaves.size > limit)
|
|
301
|
+
if (leaves.size > limit) {
|
|
238
302
|
throw new RangeError(`More than ${limit} values match`);
|
|
303
|
+
}
|
|
239
304
|
};
|
|
240
305
|
const below = (node) => {
|
|
241
|
-
if (isLeaf(node))
|
|
306
|
+
if (isLeaf(node)) {
|
|
242
307
|
return add(node.path);
|
|
243
|
-
|
|
244
|
-
|
|
308
|
+
}
|
|
309
|
+
for (const child of children(node.path)) {
|
|
310
|
+
if (!(child.kind === "target" && child.generated)) {
|
|
245
311
|
below(child);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
246
314
|
};
|
|
247
315
|
const match = (prefix, rest, wildcard) => {
|
|
248
316
|
const [head, ...tail] = rest;
|
|
249
317
|
if (head === undefined) {
|
|
250
318
|
const node = resolve(prefix) ?? (groups.has(prefix) ? { kind: "group", path: prefix } : undefined);
|
|
251
|
-
if (node)
|
|
319
|
+
if (node) {
|
|
252
320
|
below(node);
|
|
253
|
-
else if (!wildcard)
|
|
321
|
+
} else if (!wildcard) {
|
|
254
322
|
add(prefix);
|
|
323
|
+
}
|
|
255
324
|
return;
|
|
256
325
|
}
|
|
257
|
-
if (head === "*" || head ===
|
|
258
|
-
for (const child of children(prefix))
|
|
259
|
-
if (!(child.kind === "target" && child.generated) && (head === "*" || child.name.startsWith("[")))
|
|
326
|
+
if (head === "*" || head === anyIndex) {
|
|
327
|
+
for (const child of children(prefix)) {
|
|
328
|
+
if (!(child.kind === "target" && child.generated) && (head === "*" || child.name.startsWith("["))) {
|
|
260
329
|
match(child.path, tail, true);
|
|
261
|
-
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
262
333
|
match(joinPath(prefix, head), tail, wildcard);
|
|
334
|
+
}
|
|
263
335
|
};
|
|
264
|
-
for (const pattern of patterns)
|
|
336
|
+
for (const pattern of patterns) {
|
|
265
337
|
match("", pathSegments(pattern), false);
|
|
338
|
+
}
|
|
266
339
|
return [...leaves];
|
|
267
340
|
};
|
|
268
341
|
const decode = (typeId, raw) => {
|
|
269
342
|
const type = typeOfId(typeId);
|
|
270
|
-
if (type.kind === "enum")
|
|
343
|
+
if (type.kind === "enum") {
|
|
271
344
|
return Object.entries(type.members).find(([, value]) => value === raw)?.[0] ?? raw;
|
|
272
|
-
|
|
345
|
+
}
|
|
346
|
+
if (type.kind === "scalar" && type.iec === "BOOL" && typeof raw === "number") {
|
|
273
347
|
return raw !== 0;
|
|
348
|
+
}
|
|
274
349
|
return raw;
|
|
275
350
|
};
|
|
276
351
|
return { typeOf: typeOfId, resolve, children, isLeaf, expand, decode };
|
|
277
352
|
};
|
|
278
353
|
|
|
354
|
+
// packages/debug/src/timeline.ts
|
|
355
|
+
var timelineEntries = {
|
|
356
|
+
app: (at, task, sequence, record) => ({
|
|
357
|
+
at,
|
|
358
|
+
approximate: true,
|
|
359
|
+
source: "plc",
|
|
360
|
+
kind: "app",
|
|
361
|
+
task,
|
|
362
|
+
sequence,
|
|
363
|
+
record
|
|
364
|
+
}),
|
|
365
|
+
trace: (at, trace) => ({
|
|
366
|
+
at,
|
|
367
|
+
approximate: true,
|
|
368
|
+
source: "plc",
|
|
369
|
+
kind: "trace",
|
|
370
|
+
trace
|
|
371
|
+
}),
|
|
372
|
+
request: (request) => ({
|
|
373
|
+
at: request.startedAt,
|
|
374
|
+
approximate: true,
|
|
375
|
+
source: "server",
|
|
376
|
+
kind: "request",
|
|
377
|
+
request
|
|
378
|
+
}),
|
|
379
|
+
deploy: (at, details) => ({
|
|
380
|
+
at,
|
|
381
|
+
approximate: false,
|
|
382
|
+
source: "agent",
|
|
383
|
+
kind: "deploy",
|
|
384
|
+
...details
|
|
385
|
+
}),
|
|
386
|
+
audit: (at, details) => ({
|
|
387
|
+
at,
|
|
388
|
+
approximate: false,
|
|
389
|
+
source: "agent",
|
|
390
|
+
kind: "audit",
|
|
391
|
+
...details
|
|
392
|
+
}),
|
|
393
|
+
connection: (at, state, error) => ({
|
|
394
|
+
at,
|
|
395
|
+
approximate: false,
|
|
396
|
+
source: "debugger",
|
|
397
|
+
kind: "connection",
|
|
398
|
+
state,
|
|
399
|
+
...error && { error }
|
|
400
|
+
})
|
|
401
|
+
};
|
|
402
|
+
var insertInTimeOrder = (entries, entry) => {
|
|
403
|
+
for (let index = entries.length;index >= 0; index--) {
|
|
404
|
+
const before = entries[index - 1];
|
|
405
|
+
if (before === undefined || before.at <= entry.at) {
|
|
406
|
+
entries.splice(index, 0, entry);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
var createTimeline = (limit = 1e4) => {
|
|
412
|
+
const entries = [];
|
|
413
|
+
return {
|
|
414
|
+
add: (entry) => {
|
|
415
|
+
insertInTimeOrder(entries, entry);
|
|
416
|
+
if (entries.length > limit) {
|
|
417
|
+
entries.splice(0, entries.length - limit);
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
since: (at) => at ? entries.filter((entry) => entry.at > at) : [...entries]
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// packages/debug/src/requests.ts
|
|
425
|
+
var callsRemembered = 1000;
|
|
426
|
+
var createRequestFollower = () => {
|
|
427
|
+
let newest = 0;
|
|
428
|
+
const pending = new Map;
|
|
429
|
+
const calls = new Map;
|
|
430
|
+
return {
|
|
431
|
+
since: () => pending.size ? Math.min(...pending.keys()) - 1 : newest,
|
|
432
|
+
accept: (reply, changed) => {
|
|
433
|
+
if (reply.latest < newest) {
|
|
434
|
+
newest = 0;
|
|
435
|
+
pending.clear();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const oldest = reply.requests[0]?.seq;
|
|
439
|
+
if (oldest !== undefined) {
|
|
440
|
+
for (const seq of pending.keys()) {
|
|
441
|
+
if (seq < oldest) {
|
|
442
|
+
pending.delete(seq);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
for (const request of reply.requests) {
|
|
447
|
+
const waiting = pending.get(request.seq);
|
|
448
|
+
if (waiting && request.status === "pending") {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (!waiting && request.seq <= newest) {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const entry = waiting ?? timelineEntries.request(request);
|
|
455
|
+
entry.request = request;
|
|
456
|
+
newest = Math.max(newest, request.seq);
|
|
457
|
+
if (request.status === "pending") {
|
|
458
|
+
pending.set(request.seq, entry);
|
|
459
|
+
} else {
|
|
460
|
+
pending.delete(request.seq);
|
|
461
|
+
}
|
|
462
|
+
if (request.job !== undefined && request.sequence !== undefined) {
|
|
463
|
+
calls.set(`${request.job}:${request.service}:${request.sequence}`, request);
|
|
464
|
+
for (const key of [...calls.keys()].slice(0, -callsRemembered)) {
|
|
465
|
+
calls.delete(key);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
changed({ request, entry, added: !waiting });
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
call: ({
|
|
472
|
+
job,
|
|
473
|
+
service,
|
|
474
|
+
sequence
|
|
475
|
+
}) => calls.get(`${job}:${service}:${sequence}`)
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
|
|
279
479
|
// packages/debug/src/telemetry.ts
|
|
280
480
|
import { recordedAt } from "@tsplc/server/telemetry-log";
|
|
281
481
|
var clockWindowMs = 60000;
|
|
@@ -290,8 +490,9 @@ var createTelemetryFollower = (manifest) => {
|
|
|
290
490
|
let newest = 0;
|
|
291
491
|
let build = null;
|
|
292
492
|
const clock = () => {
|
|
293
|
-
if (!estimates.length)
|
|
493
|
+
if (!estimates.length) {
|
|
294
494
|
return null;
|
|
495
|
+
}
|
|
295
496
|
const offsets = estimates.map(({ offsetMs }) => offsetMs);
|
|
296
497
|
const best = estimates.reduce((best, estimate) => estimate.offsetMs > best.offsetMs ? estimate : best);
|
|
297
498
|
return {
|
|
@@ -302,8 +503,9 @@ var createTelemetryFollower = (manifest) => {
|
|
|
302
503
|
const observe = (batch, receivedAt) => {
|
|
303
504
|
const rate = rates.get(batch.task);
|
|
304
505
|
const restarted = rate !== undefined && batch.now < rate.now;
|
|
305
|
-
if (restarted)
|
|
506
|
+
if (restarted) {
|
|
306
507
|
estimates = [];
|
|
508
|
+
}
|
|
307
509
|
estimates = estimates.filter(({ at }) => receivedAt - at < clockWindowMs);
|
|
308
510
|
estimates.push({
|
|
309
511
|
offsetMs: batch.now - receivedAt,
|
|
@@ -316,9 +518,9 @@ var createTelemetryFollower = (manifest) => {
|
|
|
316
518
|
scan: batch.scan,
|
|
317
519
|
dropped: batch.dropped
|
|
318
520
|
};
|
|
319
|
-
if (!rate || restarted)
|
|
521
|
+
if (!rate || restarted) {
|
|
320
522
|
rates.set(batch.task, { scan: batch.scan, now: batch.now });
|
|
321
|
-
else if (batch.now - rate.now >= 1000) {
|
|
523
|
+
} else if (batch.now - rate.now >= 1000) {
|
|
322
524
|
stats.scansPerSecond = (batch.scan - rate.scan) * 1000 / (batch.now - rate.now);
|
|
323
525
|
rates.set(batch.task, { scan: batch.scan, now: batch.now });
|
|
324
526
|
}
|
|
@@ -333,8 +535,9 @@ var createTelemetryFollower = (manifest) => {
|
|
|
333
535
|
}
|
|
334
536
|
const received = [];
|
|
335
537
|
for (const batch of reply.batches) {
|
|
336
|
-
if (batch.seq <= newest)
|
|
538
|
+
if (batch.seq <= newest) {
|
|
337
539
|
continue;
|
|
540
|
+
}
|
|
338
541
|
newest = batch.seq;
|
|
339
542
|
build = batch.build;
|
|
340
543
|
const receivedAt = new Date(batch.receivedAt);
|
|
@@ -355,69 +558,6 @@ var createTelemetryFollower = (manifest) => {
|
|
|
355
558
|
};
|
|
356
559
|
};
|
|
357
560
|
|
|
358
|
-
// packages/debug/src/timeline.ts
|
|
359
|
-
var timelineEntries = {
|
|
360
|
-
app: (at, task, sequence, record) => ({
|
|
361
|
-
at,
|
|
362
|
-
approximate: true,
|
|
363
|
-
source: "plc",
|
|
364
|
-
kind: "app",
|
|
365
|
-
task,
|
|
366
|
-
sequence,
|
|
367
|
-
record
|
|
368
|
-
}),
|
|
369
|
-
trace: (at, trace) => ({
|
|
370
|
-
at,
|
|
371
|
-
approximate: true,
|
|
372
|
-
source: "plc",
|
|
373
|
-
kind: "trace",
|
|
374
|
-
trace
|
|
375
|
-
}),
|
|
376
|
-
request: (request) => ({
|
|
377
|
-
at: request.startedAt,
|
|
378
|
-
approximate: true,
|
|
379
|
-
source: "server",
|
|
380
|
-
kind: "request",
|
|
381
|
-
request
|
|
382
|
-
}),
|
|
383
|
-
deploy: (at, details) => ({
|
|
384
|
-
at,
|
|
385
|
-
approximate: false,
|
|
386
|
-
source: "agent",
|
|
387
|
-
kind: "deploy",
|
|
388
|
-
...details
|
|
389
|
-
}),
|
|
390
|
-
audit: (at, details) => ({
|
|
391
|
-
at,
|
|
392
|
-
approximate: false,
|
|
393
|
-
source: "agent",
|
|
394
|
-
kind: "audit",
|
|
395
|
-
...details
|
|
396
|
-
}),
|
|
397
|
-
connection: (at, state, error) => ({
|
|
398
|
-
at,
|
|
399
|
-
approximate: false,
|
|
400
|
-
source: "debugger",
|
|
401
|
-
kind: "connection",
|
|
402
|
-
state,
|
|
403
|
-
...error && { error }
|
|
404
|
-
})
|
|
405
|
-
};
|
|
406
|
-
var createTimeline = (limit = 1e4) => {
|
|
407
|
-
const entries = [];
|
|
408
|
-
return {
|
|
409
|
-
add: (entry) => {
|
|
410
|
-
let index = entries.length;
|
|
411
|
-
while (index > 0 && entry.at < (entries[index - 1]?.at ?? entry.at))
|
|
412
|
-
index--;
|
|
413
|
-
entries.splice(index, 0, entry);
|
|
414
|
-
if (entries.length > limit)
|
|
415
|
-
entries.splice(0, entries.length - limit);
|
|
416
|
-
},
|
|
417
|
-
since: (at) => at ? entries.filter((entry) => entry.at > at) : [...entries]
|
|
418
|
-
};
|
|
419
|
-
};
|
|
420
|
-
|
|
421
561
|
// packages/debug/src/trace.ts
|
|
422
562
|
var createTraceDecoder = (manifest, paths) => {
|
|
423
563
|
const sites = new Map(manifest.events.map((site) => [site.id, site]));
|
|
@@ -429,8 +569,9 @@ var createTraceDecoder = (manifest, paths) => {
|
|
|
429
569
|
return {
|
|
430
570
|
decode: (record, task, sequence) => {
|
|
431
571
|
const site = sites.get(record.site);
|
|
432
|
-
if (!site)
|
|
572
|
+
if (!site) {
|
|
433
573
|
return;
|
|
574
|
+
}
|
|
434
575
|
return {
|
|
435
576
|
sequence,
|
|
436
577
|
task,
|
|
@@ -457,7 +598,7 @@ import { dirname } from "node:path";
|
|
|
457
598
|
import { isDeepStrictEqual } from "node:util";
|
|
458
599
|
import { z } from "zod";
|
|
459
600
|
var watchGroupsSchema = z.strictObject({
|
|
460
|
-
groups: z.record(z.string().
|
|
601
|
+
groups: z.record(z.string().nonempty(), z.array(z.string().nonempty()))
|
|
461
602
|
});
|
|
462
603
|
var loadWatchGroups = async (file, configured) => {
|
|
463
604
|
let saved = existsSync(file) ? watchGroupsSchema.parse(JSON.parse(await readFile(file, "utf8"))).groups : {};
|
|
@@ -485,40 +626,22 @@ var pullMs = 1000;
|
|
|
485
626
|
var buildCheckMs = 1e4;
|
|
486
627
|
var hardwareMs = 5000;
|
|
487
628
|
var detailedJobs = 16;
|
|
488
|
-
var requestsRemembered = 1000;
|
|
489
|
-
var requestTrace = z2.object({
|
|
490
|
-
seq: z2.number().int(),
|
|
491
|
-
service: z2.string(),
|
|
492
|
-
job: z2.number().int().optional(),
|
|
493
|
-
sequence: z2.number().int().optional(),
|
|
494
|
-
startedAt: z2.string(),
|
|
495
|
-
durationMs: z2.number().optional(),
|
|
496
|
-
status: z2.enum(["pending", "ok", "error", "timeout"]),
|
|
497
|
-
httpStatus: z2.number().int().optional(),
|
|
498
|
-
error: z2.string().optional(),
|
|
499
|
-
input: z2.unknown().optional(),
|
|
500
|
-
output: z2.unknown().optional()
|
|
501
|
-
});
|
|
502
|
-
var requestsReply = z2.object({ requests: z2.array(requestTrace), latest: z2.number().int() });
|
|
503
|
-
var telemetryReply = z2.object({
|
|
504
|
-
batches: z2.array(telemetryBatch.extend({ seq: z2.number().int(), receivedAt: z2.string() })),
|
|
505
|
-
latest: z2.number().int()
|
|
506
|
-
});
|
|
507
|
-
var message = (error) => error instanceof Error ? error.message : String(error);
|
|
508
629
|
var createShown = () => {
|
|
509
630
|
const counts = new Map;
|
|
510
631
|
return {
|
|
511
632
|
has: (key) => counts.has(key),
|
|
512
633
|
add: (keys) => {
|
|
513
|
-
for (const key of keys)
|
|
634
|
+
for (const key of keys) {
|
|
514
635
|
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
636
|
+
}
|
|
515
637
|
return () => {
|
|
516
638
|
for (const key of keys) {
|
|
517
639
|
const count = counts.get(key) - 1;
|
|
518
|
-
if (count)
|
|
640
|
+
if (count) {
|
|
519
641
|
counts.set(key, count);
|
|
520
|
-
else
|
|
642
|
+
} else {
|
|
521
643
|
counts.delete(key);
|
|
644
|
+
}
|
|
522
645
|
}
|
|
523
646
|
};
|
|
524
647
|
}
|
|
@@ -529,6 +652,7 @@ var createDebugger = async (options) => {
|
|
|
529
652
|
const paths = createPaths(manifest);
|
|
530
653
|
const trace = createTraceDecoder(manifest, paths);
|
|
531
654
|
const telemetry = createTelemetryFollower(manifest);
|
|
655
|
+
const requests = createRequestFollower();
|
|
532
656
|
const timeline = createTimeline();
|
|
533
657
|
const watchGroups = await loadWatchGroups(join(options.stateDirectory, "watch.json"), options.watch);
|
|
534
658
|
const listeners = new Set;
|
|
@@ -536,15 +660,17 @@ var createDebugger = async (options) => {
|
|
|
536
660
|
const { signal } = controller;
|
|
537
661
|
let controllerBuild = null;
|
|
538
662
|
const emit = (message) => {
|
|
539
|
-
for (const listener of listeners)
|
|
663
|
+
for (const listener of listeners) {
|
|
540
664
|
listener(message);
|
|
665
|
+
}
|
|
541
666
|
};
|
|
542
667
|
const record = (entry) => {
|
|
543
668
|
timeline.add(entry);
|
|
544
669
|
emit({ event: "timeline", data: entry });
|
|
545
670
|
};
|
|
546
|
-
for (const entry of options.timeline ?? [])
|
|
671
|
+
for (const entry of options.timeline ?? []) {
|
|
547
672
|
timeline.add(entry);
|
|
673
|
+
}
|
|
548
674
|
const session = () => ({
|
|
549
675
|
build: {
|
|
550
676
|
manifest: manifest.build.id,
|
|
@@ -559,8 +685,9 @@ var createDebugger = async (options) => {
|
|
|
559
685
|
...options.projectRoot && { projectRoot: options.projectRoot }
|
|
560
686
|
});
|
|
561
687
|
const setBuild = (build) => {
|
|
562
|
-
if (build === controllerBuild)
|
|
688
|
+
if (build === controllerBuild) {
|
|
563
689
|
return;
|
|
690
|
+
}
|
|
564
691
|
controllerBuild = build;
|
|
565
692
|
emit({ event: "session", data: session() });
|
|
566
693
|
};
|
|
@@ -573,9 +700,10 @@ var createDebugger = async (options) => {
|
|
|
573
700
|
});
|
|
574
701
|
const locate = (path) => {
|
|
575
702
|
const resolved = paths.resolve(path);
|
|
576
|
-
if (!resolved)
|
|
703
|
+
if (!resolved) {
|
|
577
704
|
return unavailable(path, "Unknown path");
|
|
578
|
-
|
|
705
|
+
}
|
|
706
|
+
if (resolved.kind === "constant") {
|
|
579
707
|
return {
|
|
580
708
|
path,
|
|
581
709
|
value: resolved.value,
|
|
@@ -583,11 +711,16 @@ var createDebugger = async (options) => {
|
|
|
583
711
|
status: "Constant",
|
|
584
712
|
receivedAt: new Date().toISOString()
|
|
585
713
|
};
|
|
586
|
-
|
|
714
|
+
}
|
|
715
|
+
if (!resolved.readable) {
|
|
587
716
|
return unavailable(path, "Not readable");
|
|
717
|
+
}
|
|
588
718
|
return resolved;
|
|
589
719
|
};
|
|
590
|
-
const targetRead = ({
|
|
720
|
+
const targetRead = ({
|
|
721
|
+
target,
|
|
722
|
+
type
|
|
723
|
+
}) => ({
|
|
591
724
|
target,
|
|
592
725
|
type: paths.typeOf(type)
|
|
593
726
|
});
|
|
@@ -605,38 +738,51 @@ var createDebugger = async (options) => {
|
|
|
605
738
|
listener(located);
|
|
606
739
|
continue;
|
|
607
740
|
}
|
|
608
|
-
if (!watched.has(located.target))
|
|
741
|
+
if (!watched.has(located.target)) {
|
|
609
742
|
reads.push(targetRead(located));
|
|
610
|
-
|
|
743
|
+
}
|
|
744
|
+
watched.set(located.target, [
|
|
745
|
+
...watched.get(located.target) ?? [],
|
|
746
|
+
{ path, ...located }
|
|
747
|
+
]);
|
|
611
748
|
}
|
|
612
|
-
if (!reads.length)
|
|
749
|
+
if (!reads.length) {
|
|
613
750
|
return () => {};
|
|
751
|
+
}
|
|
614
752
|
return adapter.subscribe(reads, (target, received) => {
|
|
615
|
-
for (const { path, type } of watched.get(target) ?? [])
|
|
753
|
+
for (const { path, type } of watched.get(target) ?? []) {
|
|
616
754
|
listener(sampleOf(path, type, received));
|
|
755
|
+
}
|
|
617
756
|
});
|
|
618
757
|
};
|
|
619
758
|
const values = async (patterns) => {
|
|
620
759
|
const connection = adapter.connection();
|
|
621
|
-
if (connection.state !== "connected")
|
|
760
|
+
if (connection.state !== "connected") {
|
|
622
761
|
throw new DebugError(connection.error ?? "The controller is not connected", 503);
|
|
762
|
+
}
|
|
623
763
|
const located = paths.expand(patterns).map((path) => ({ path, located: locate(path) }));
|
|
624
764
|
const read = await adapter.read(located.flatMap(({ located }) => ("target" in located) ? [targetRead(located)] : []));
|
|
625
765
|
return located.map(({ path, located }) => {
|
|
626
|
-
if (!("target" in located))
|
|
766
|
+
if (!("target" in located)) {
|
|
627
767
|
return located;
|
|
768
|
+
}
|
|
628
769
|
const sample = read.get(located.target);
|
|
629
770
|
return sample ? sampleOf(path, located.type, sample) : unavailable(path, "Not read");
|
|
630
771
|
});
|
|
631
772
|
};
|
|
632
|
-
const stopBuild = adapter.subscribe([
|
|
633
|
-
|
|
773
|
+
const stopBuild = adapter.subscribe([
|
|
774
|
+
{
|
|
775
|
+
target: manifest.build.target,
|
|
776
|
+
type: { kind: "scalar", iec: "STRING" }
|
|
777
|
+
}
|
|
778
|
+
], (_, sample) => {
|
|
779
|
+
if (sample.quality === "good" && typeof sample.value === "string") {
|
|
634
780
|
setBuild(sample.value);
|
|
781
|
+
}
|
|
635
782
|
}, buildCheckMs);
|
|
636
783
|
const latest = new Map;
|
|
637
784
|
const traced = new Map;
|
|
638
785
|
const failures = new Map;
|
|
639
|
-
const requests = new Map;
|
|
640
786
|
let unread = new Set;
|
|
641
787
|
const shownJobs = createShown();
|
|
642
788
|
const shownHealth = createShown();
|
|
@@ -649,35 +795,41 @@ var createDebugger = async (options) => {
|
|
|
649
795
|
unread: unread.has(job.id),
|
|
650
796
|
clock: telemetry.clock(),
|
|
651
797
|
now: Date.now(),
|
|
652
|
-
request:
|
|
798
|
+
request: requests.call
|
|
653
799
|
});
|
|
654
|
-
const deviceOf = (
|
|
800
|
+
const deviceOf = (device) => deviceState(device, manifest.io, (target) => latest.get(target));
|
|
655
801
|
const send = (key, message) => {
|
|
656
802
|
const json = JSON.stringify(message.data);
|
|
657
|
-
if (sent.get(key) === json)
|
|
803
|
+
if (sent.get(key) === json) {
|
|
658
804
|
return;
|
|
805
|
+
}
|
|
659
806
|
sent.set(key, json);
|
|
660
807
|
emit(message);
|
|
661
808
|
};
|
|
662
809
|
const updateJob = (job) => send(`job:${job.id}`, { event: "job", data: jobOf(job) });
|
|
663
|
-
const updateDevice = (
|
|
810
|
+
const updateDevice = (device) => send(`device:${device.id}`, { event: "device", data: deviceOf(device) });
|
|
664
811
|
const reader = (intervalMs, update) => {
|
|
665
812
|
let current = { key: "", stop: () => {} };
|
|
666
813
|
return (reads) => {
|
|
667
814
|
const key = reads.map(({ read }) => read.target).join(`
|
|
668
815
|
`);
|
|
669
|
-
if (key === current.key)
|
|
816
|
+
if (key === current.key) {
|
|
670
817
|
return;
|
|
818
|
+
}
|
|
671
819
|
current.stop();
|
|
672
820
|
const owners = new Map;
|
|
673
|
-
for (const { owner, read } of reads)
|
|
821
|
+
for (const { owner, read } of reads) {
|
|
674
822
|
owners.set(read.target, [...owners.get(read.target) ?? [], owner]);
|
|
823
|
+
}
|
|
675
824
|
current = {
|
|
676
825
|
key,
|
|
677
|
-
stop: reads.length ? adapter.subscribe([
|
|
826
|
+
stop: reads.length ? adapter.subscribe([
|
|
827
|
+
...new Map(reads.map(({ read }) => [read.target, read])).values()
|
|
828
|
+
], (target, sample) => {
|
|
678
829
|
latest.set(target, sample);
|
|
679
|
-
for (const owner of owners.get(target) ?? [])
|
|
830
|
+
for (const owner of owners.get(target) ?? []) {
|
|
680
831
|
update(owner);
|
|
832
|
+
}
|
|
681
833
|
schedule();
|
|
682
834
|
}, intervalMs) : () => {}
|
|
683
835
|
};
|
|
@@ -685,6 +837,15 @@ var createDebugger = async (options) => {
|
|
|
685
837
|
};
|
|
686
838
|
const readJobs = reader(adapter.samplingMs, updateJob);
|
|
687
839
|
const readDevices = reader(hardwareMs, updateDevice);
|
|
840
|
+
const shownDeviceReads = (device) => {
|
|
841
|
+
if (shownDevices.has(device.id)) {
|
|
842
|
+
return deviceReads(device, manifest.io, paths.typeOf);
|
|
843
|
+
}
|
|
844
|
+
if (shownHealth.has(device.id)) {
|
|
845
|
+
return healthReads(device);
|
|
846
|
+
}
|
|
847
|
+
return [];
|
|
848
|
+
};
|
|
688
849
|
const plan = () => {
|
|
689
850
|
const shown = manifest.jobs.filter((job) => shownJobs.has(job.id));
|
|
690
851
|
const busy = shown.filter((job) => busyOf(job, (target) => latest.get(target), traced.get(job.id)));
|
|
@@ -693,12 +854,13 @@ var createDebugger = async (options) => {
|
|
|
693
854
|
...shown.map((job) => ({ owner: job, read: busyRead(job) })),
|
|
694
855
|
...busy.slice(0, detailedJobs).flatMap((job) => detailReads(job).map((read) => ({ owner: job, read })))
|
|
695
856
|
]);
|
|
696
|
-
readDevices(manifest.devices.flatMap((device) => (
|
|
857
|
+
readDevices(manifest.devices.flatMap((device) => shownDeviceReads(device).map((read) => ({ owner: device, read }))));
|
|
697
858
|
};
|
|
698
859
|
let planned = false;
|
|
699
860
|
const schedule = () => {
|
|
700
|
-
if (planned)
|
|
861
|
+
if (planned) {
|
|
701
862
|
return;
|
|
863
|
+
}
|
|
702
864
|
planned = true;
|
|
703
865
|
queueMicrotask(() => {
|
|
704
866
|
planned = false;
|
|
@@ -713,33 +875,48 @@ var createDebugger = async (options) => {
|
|
|
713
875
|
];
|
|
714
876
|
schedule();
|
|
715
877
|
return () => {
|
|
716
|
-
for (const stop of stops)
|
|
878
|
+
for (const stop of stops) {
|
|
717
879
|
stop();
|
|
880
|
+
}
|
|
718
881
|
schedule();
|
|
719
882
|
};
|
|
720
883
|
};
|
|
721
|
-
const receive = ({
|
|
884
|
+
const receive = ({
|
|
885
|
+
build,
|
|
886
|
+
task,
|
|
887
|
+
sequence,
|
|
888
|
+
at,
|
|
889
|
+
record: received
|
|
890
|
+
}) => {
|
|
722
891
|
if (received.kind === "trace") {
|
|
723
|
-
if (build !== manifest.build.id)
|
|
892
|
+
if (build !== manifest.build.id) {
|
|
724
893
|
return;
|
|
894
|
+
}
|
|
725
895
|
const event = trace.decode(received, task, sequence);
|
|
726
|
-
if (event)
|
|
896
|
+
if (event) {
|
|
727
897
|
record(timelineEntries.trace(at, event));
|
|
898
|
+
}
|
|
728
899
|
const site = trace.job(received);
|
|
729
|
-
if (!site)
|
|
900
|
+
if (!site) {
|
|
730
901
|
return;
|
|
902
|
+
}
|
|
731
903
|
const startedAt = site.started ? at : traced.get(site.job.id)?.startedAt;
|
|
732
|
-
traced.set(site.job.id, {
|
|
904
|
+
traced.set(site.job.id, {
|
|
905
|
+
busy: site.started,
|
|
906
|
+
...startedAt && { startedAt }
|
|
907
|
+
});
|
|
733
908
|
updateJob(site.job);
|
|
734
909
|
schedule();
|
|
735
910
|
return;
|
|
736
911
|
}
|
|
737
912
|
record(timelineEntries.app(at, task, sequence, received));
|
|
738
|
-
if (received.kind !== "exception" || received.handled)
|
|
913
|
+
if (received.kind !== "exception" || received.handled) {
|
|
739
914
|
return;
|
|
915
|
+
}
|
|
740
916
|
const job = manifest.jobs.find(({ name, instance }) => name === received.function && instance === received.instance);
|
|
741
|
-
if (!job)
|
|
917
|
+
if (!job) {
|
|
742
918
|
return;
|
|
919
|
+
}
|
|
743
920
|
failures.set(job.id, {
|
|
744
921
|
at,
|
|
745
922
|
code: received.code,
|
|
@@ -749,81 +926,69 @@ var createDebugger = async (options) => {
|
|
|
749
926
|
updateJob(job);
|
|
750
927
|
};
|
|
751
928
|
const pull = async (url, token) => {
|
|
752
|
-
const response = await fetch(url, {
|
|
753
|
-
|
|
929
|
+
const response = await fetch(url, {
|
|
930
|
+
headers: { authorization: `Bearer ${token}` },
|
|
931
|
+
signal
|
|
932
|
+
});
|
|
933
|
+
if (!response.ok) {
|
|
754
934
|
throw new Error(`The server answered ${response.status}`);
|
|
935
|
+
}
|
|
755
936
|
return response.json();
|
|
756
937
|
};
|
|
757
|
-
const pullTelemetry = async ({
|
|
938
|
+
const pullTelemetry = async ({
|
|
939
|
+
url,
|
|
940
|
+
token
|
|
941
|
+
}) => {
|
|
758
942
|
const reply = telemetryReply.parse(await pull(new URL(`/tsplc/telemetry?since=${telemetry.since()}`, url), token));
|
|
759
943
|
const received = telemetry.accept(reply);
|
|
760
|
-
for (const item of received)
|
|
944
|
+
for (const item of received) {
|
|
761
945
|
receive(item);
|
|
946
|
+
}
|
|
762
947
|
const build = telemetry.build();
|
|
763
|
-
if (build)
|
|
948
|
+
if (build) {
|
|
764
949
|
setBuild(build);
|
|
765
|
-
|
|
950
|
+
}
|
|
951
|
+
if (reply.batches.length) {
|
|
766
952
|
emit({ event: "session", data: session() });
|
|
953
|
+
}
|
|
767
954
|
};
|
|
768
|
-
const pullRequests = ({
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
if (
|
|
775
|
-
|
|
776
|
-
pending.clear();
|
|
777
|
-
return;
|
|
955
|
+
const pullRequests = async ({
|
|
956
|
+
url,
|
|
957
|
+
token
|
|
958
|
+
}) => {
|
|
959
|
+
const reply = requestsReply.parse(await pull(new URL(`/tsplc/debug/requests?since=${requests.since()}`, url), token));
|
|
960
|
+
requests.accept(reply, ({ request, entry, added }) => {
|
|
961
|
+
if (added) {
|
|
962
|
+
timeline.add(entry);
|
|
778
963
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
pending.delete(seq);
|
|
964
|
+
emit({ event: "request", data: request });
|
|
965
|
+
emit({ event: "timeline", data: entry });
|
|
966
|
+
if (request.job === undefined || request.sequence === undefined) {
|
|
967
|
+
return;
|
|
784
968
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
continue;
|
|
789
|
-
if (!waiting && request.seq <= newest)
|
|
790
|
-
continue;
|
|
791
|
-
const entry = waiting ?? timelineEntries.request(request);
|
|
792
|
-
entry.request = request;
|
|
793
|
-
newest = Math.max(newest, request.seq);
|
|
794
|
-
if (request.status === "pending")
|
|
795
|
-
pending.set(request.seq, entry);
|
|
796
|
-
else
|
|
797
|
-
pending.delete(request.seq);
|
|
798
|
-
if (!waiting)
|
|
799
|
-
timeline.add(entry);
|
|
800
|
-
emit({ event: "request", data: request });
|
|
801
|
-
emit({ event: "timeline", data: entry });
|
|
802
|
-
if (request.job === undefined || request.sequence === undefined)
|
|
803
|
-
continue;
|
|
804
|
-
requests.set(`${request.job}:${request.service}:${request.sequence}`, request);
|
|
805
|
-
for (const key of [...requests.keys()].slice(0, -requestsRemembered))
|
|
806
|
-
requests.delete(key);
|
|
807
|
-
const job = manifest.jobs.find(({ id }) => id === request.job);
|
|
808
|
-
if (job)
|
|
809
|
-
updateJob(job);
|
|
969
|
+
const job = manifest.jobs.find(({ id }) => id === request.job);
|
|
970
|
+
if (job) {
|
|
971
|
+
updateJob(job);
|
|
810
972
|
}
|
|
811
|
-
};
|
|
973
|
+
});
|
|
812
974
|
};
|
|
813
975
|
const stopConnection = adapter.onConnection((connection) => {
|
|
814
976
|
record(timelineEntries.connection(connection.since, connection.state, connection.error));
|
|
815
977
|
emit({ event: "session", data: session() });
|
|
816
978
|
if (connection.state !== "connected") {
|
|
817
|
-
for (const [target, sample] of latest)
|
|
979
|
+
for (const [target, sample] of latest) {
|
|
818
980
|
latest.set(target, {
|
|
819
981
|
...sample,
|
|
820
982
|
quality: "unavailable",
|
|
821
983
|
status: connection.error ?? "The controller is not connected"
|
|
822
984
|
});
|
|
823
|
-
|
|
985
|
+
}
|
|
986
|
+
for (const job of manifest.jobs) {
|
|
824
987
|
updateJob(job);
|
|
825
|
-
|
|
826
|
-
|
|
988
|
+
}
|
|
989
|
+
for (const device of manifest.devices) {
|
|
990
|
+
updateDevice(device);
|
|
991
|
+
}
|
|
827
992
|
}
|
|
828
993
|
});
|
|
829
994
|
const repeat = async (name, intervalMs, work) => {
|
|
@@ -833,11 +998,14 @@ var createDebugger = async (options) => {
|
|
|
833
998
|
await work();
|
|
834
999
|
failure = undefined;
|
|
835
1000
|
} catch (error) {
|
|
836
|
-
if (signal.aborted)
|
|
1001
|
+
if (signal.aborted) {
|
|
837
1002
|
return;
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1003
|
+
}
|
|
1004
|
+
const message = errorMessage(error);
|
|
1005
|
+
if (message !== failure) {
|
|
1006
|
+
options.log(`${name} failed: ${message}`);
|
|
1007
|
+
}
|
|
1008
|
+
failure = message;
|
|
841
1009
|
}
|
|
842
1010
|
await delay(intervalMs, undefined, { signal }).catch(() => {
|
|
843
1011
|
return;
|
|
@@ -847,7 +1015,7 @@ var createDebugger = async (options) => {
|
|
|
847
1015
|
const { server } = options;
|
|
848
1016
|
const running = server ? [
|
|
849
1017
|
repeat("Reading the server's telemetry", pullMs, () => pullTelemetry(server)),
|
|
850
|
-
repeat("Reading the server's requests", pullMs, pullRequests(server))
|
|
1018
|
+
repeat("Reading the server's requests", pullMs, () => pullRequests(server))
|
|
851
1019
|
] : [];
|
|
852
1020
|
return {
|
|
853
1021
|
manifest,
|
|
@@ -855,7 +1023,7 @@ var createDebugger = async (options) => {
|
|
|
855
1023
|
session,
|
|
856
1024
|
values,
|
|
857
1025
|
jobs: () => manifest.jobs.map(jobOf),
|
|
858
|
-
hardware: () => manifest.devices.map(
|
|
1026
|
+
hardware: () => manifest.devices.map(deviceOf),
|
|
859
1027
|
timeline: (since) => timeline.since(since),
|
|
860
1028
|
watchGroups: () => watchGroups.groups(),
|
|
861
1029
|
saveWatchGroups: (groups) => watchGroups.save(groups),
|
|
@@ -879,189 +1047,193 @@ var createDebugger = async (options) => {
|
|
|
879
1047
|
};
|
|
880
1048
|
// packages/debug/src/http.ts
|
|
881
1049
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
882
|
-
import { z as
|
|
1050
|
+
import { z as z3 } from "zod";
|
|
883
1051
|
|
|
884
1052
|
// packages/debug/src/manifest.ts
|
|
885
|
-
import { z as
|
|
886
|
-
var sourceRange =
|
|
887
|
-
file:
|
|
888
|
-
line:
|
|
889
|
-
column:
|
|
890
|
-
endLine:
|
|
891
|
-
endColumn:
|
|
1053
|
+
import { z as z2 } from "zod";
|
|
1054
|
+
var sourceRange = z2.object({
|
|
1055
|
+
file: z2.number().int(),
|
|
1056
|
+
line: z2.number().int(),
|
|
1057
|
+
column: z2.number().int(),
|
|
1058
|
+
endLine: z2.number().int(),
|
|
1059
|
+
endColumn: z2.number().int()
|
|
892
1060
|
});
|
|
893
|
-
var json =
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1061
|
+
var json = z2.lazy(() => z2.union([
|
|
1062
|
+
z2.string(),
|
|
1063
|
+
z2.number(),
|
|
1064
|
+
z2.boolean(),
|
|
1065
|
+
z2.null(),
|
|
1066
|
+
z2.array(json),
|
|
1067
|
+
z2.record(z2.string(), json)
|
|
900
1068
|
]));
|
|
901
|
-
var debugField =
|
|
902
|
-
name:
|
|
903
|
-
target:
|
|
904
|
-
type:
|
|
1069
|
+
var debugField = z2.object({
|
|
1070
|
+
name: z2.string(),
|
|
1071
|
+
target: z2.string(),
|
|
1072
|
+
type: z2.string(),
|
|
905
1073
|
declaration: sourceRange.optional(),
|
|
906
|
-
generated:
|
|
1074
|
+
generated: z2.literal(true).optional()
|
|
907
1075
|
});
|
|
908
|
-
var debugType =
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
kind:
|
|
912
|
-
name:
|
|
913
|
-
members:
|
|
1076
|
+
var debugType = z2.discriminatedUnion("kind", [
|
|
1077
|
+
z2.object({ kind: z2.literal("scalar"), iec: z2.string() }),
|
|
1078
|
+
z2.object({
|
|
1079
|
+
kind: z2.literal("enum"),
|
|
1080
|
+
name: z2.string(),
|
|
1081
|
+
members: z2.record(z2.string(), z2.number().int())
|
|
914
1082
|
}),
|
|
915
|
-
|
|
916
|
-
kind:
|
|
917
|
-
name:
|
|
1083
|
+
z2.object({
|
|
1084
|
+
kind: z2.literal("record"),
|
|
1085
|
+
name: z2.string(),
|
|
918
1086
|
declaration: sourceRange.optional(),
|
|
919
|
-
fields:
|
|
1087
|
+
fields: z2.array(debugField)
|
|
920
1088
|
}),
|
|
921
|
-
|
|
922
|
-
kind:
|
|
923
|
-
element:
|
|
924
|
-
length:
|
|
925
|
-
lower:
|
|
1089
|
+
z2.object({
|
|
1090
|
+
kind: z2.literal("array"),
|
|
1091
|
+
element: z2.string(),
|
|
1092
|
+
length: z2.number().int(),
|
|
1093
|
+
lower: z2.number().int()
|
|
926
1094
|
})
|
|
927
1095
|
]);
|
|
928
|
-
var debugRoot =
|
|
929
|
-
path:
|
|
930
|
-
target:
|
|
931
|
-
type:
|
|
1096
|
+
var debugRoot = z2.object({
|
|
1097
|
+
path: z2.string(),
|
|
1098
|
+
target: z2.string(),
|
|
1099
|
+
type: z2.string(),
|
|
932
1100
|
declaration: sourceRange.optional(),
|
|
933
|
-
readable:
|
|
934
|
-
generated:
|
|
1101
|
+
readable: z2.boolean(),
|
|
1102
|
+
generated: z2.literal(true).optional()
|
|
935
1103
|
});
|
|
936
|
-
var debugConstant =
|
|
937
|
-
path:
|
|
938
|
-
type:
|
|
1104
|
+
var debugConstant = z2.object({
|
|
1105
|
+
path: z2.string(),
|
|
1106
|
+
type: z2.string(),
|
|
939
1107
|
value: json,
|
|
940
1108
|
declaration: sourceRange.optional()
|
|
941
1109
|
});
|
|
942
|
-
var debugIo =
|
|
943
|
-
path:
|
|
944
|
-
target:
|
|
945
|
-
direction:
|
|
946
|
-
type:
|
|
947
|
-
device:
|
|
948
|
-
channel:
|
|
949
|
-
address:
|
|
950
|
-
inverted:
|
|
951
|
-
shared:
|
|
1110
|
+
var debugIo = z2.object({
|
|
1111
|
+
path: z2.string(),
|
|
1112
|
+
target: z2.string(),
|
|
1113
|
+
direction: z2.enum(["input", "output"]),
|
|
1114
|
+
type: z2.string(),
|
|
1115
|
+
device: z2.string().optional(),
|
|
1116
|
+
channel: z2.string().optional(),
|
|
1117
|
+
address: z2.string().optional(),
|
|
1118
|
+
inverted: z2.literal(true).optional(),
|
|
1119
|
+
shared: z2.literal(true).optional()
|
|
952
1120
|
});
|
|
953
|
-
var debugChannel =
|
|
954
|
-
id:
|
|
955
|
-
name:
|
|
956
|
-
direction:
|
|
957
|
-
type:
|
|
958
|
-
offsetBits:
|
|
959
|
-
bits:
|
|
960
|
-
address:
|
|
961
|
-
unit:
|
|
962
|
-
description:
|
|
1121
|
+
var debugChannel = z2.object({
|
|
1122
|
+
id: z2.string(),
|
|
1123
|
+
name: z2.string(),
|
|
1124
|
+
direction: z2.enum(["input", "output"]),
|
|
1125
|
+
type: z2.string(),
|
|
1126
|
+
offsetBits: z2.number().int(),
|
|
1127
|
+
bits: z2.number().int(),
|
|
1128
|
+
address: z2.string().optional(),
|
|
1129
|
+
unit: z2.string().optional(),
|
|
1130
|
+
description: z2.string().optional()
|
|
963
1131
|
});
|
|
964
|
-
var debugDevice =
|
|
965
|
-
id:
|
|
966
|
-
product:
|
|
967
|
-
vendor:
|
|
968
|
-
productCode:
|
|
969
|
-
network:
|
|
970
|
-
connection:
|
|
971
|
-
assemblies:
|
|
972
|
-
direction:
|
|
973
|
-
bytes:
|
|
974
|
-
address:
|
|
975
|
-
raw:
|
|
1132
|
+
var debugDevice = z2.object({
|
|
1133
|
+
id: z2.string(),
|
|
1134
|
+
product: z2.string().optional(),
|
|
1135
|
+
vendor: z2.string().optional(),
|
|
1136
|
+
productCode: z2.string().optional(),
|
|
1137
|
+
network: z2.object({ address: z2.string(), rpiMs: z2.number().optional() }).optional(),
|
|
1138
|
+
connection: z2.string().optional(),
|
|
1139
|
+
assemblies: z2.array(z2.object({
|
|
1140
|
+
direction: z2.enum(["input", "output"]),
|
|
1141
|
+
bytes: z2.number().int(),
|
|
1142
|
+
address: z2.string().optional(),
|
|
1143
|
+
raw: z2.string().optional()
|
|
976
1144
|
})),
|
|
977
|
-
health:
|
|
978
|
-
state:
|
|
979
|
-
states:
|
|
980
|
-
diagnostic:
|
|
981
|
-
message:
|
|
1145
|
+
health: z2.object({
|
|
1146
|
+
state: z2.string().optional(),
|
|
1147
|
+
states: z2.record(z2.string(), z2.number().int()).optional(),
|
|
1148
|
+
diagnostic: z2.string().optional(),
|
|
1149
|
+
message: z2.string().optional()
|
|
982
1150
|
}).optional(),
|
|
983
|
-
status:
|
|
984
|
-
channels:
|
|
1151
|
+
status: z2.record(z2.string(), z2.string()).optional(),
|
|
1152
|
+
channels: z2.array(debugChannel)
|
|
985
1153
|
});
|
|
986
|
-
var debugFunction =
|
|
987
|
-
id:
|
|
988
|
-
name:
|
|
1154
|
+
var debugFunction = z2.object({
|
|
1155
|
+
id: z2.number().int(),
|
|
1156
|
+
name: z2.string(),
|
|
989
1157
|
declaration: sourceRange,
|
|
990
|
-
instance:
|
|
991
|
-
task:
|
|
992
|
-
target:
|
|
993
|
-
reads:
|
|
1158
|
+
instance: z2.string().optional(),
|
|
1159
|
+
task: z2.string().optional(),
|
|
1160
|
+
target: z2.string(),
|
|
1161
|
+
reads: z2.array(z2.object({ range: sourceRange, path: z2.string() }))
|
|
994
1162
|
});
|
|
995
|
-
var debugJob =
|
|
996
|
-
id:
|
|
997
|
-
name:
|
|
1163
|
+
var debugJob = z2.object({
|
|
1164
|
+
id: z2.number().int(),
|
|
1165
|
+
name: z2.string(),
|
|
998
1166
|
declaration: sourceRange,
|
|
999
|
-
instance:
|
|
1000
|
-
target:
|
|
1001
|
-
steps:
|
|
1002
|
-
step:
|
|
1003
|
-
kind:
|
|
1167
|
+
instance: z2.string().optional(),
|
|
1168
|
+
target: z2.string(),
|
|
1169
|
+
steps: z2.array(z2.object({
|
|
1170
|
+
step: z2.number().int(),
|
|
1171
|
+
kind: z2.enum(["await", "return", "throw", "statement"]),
|
|
1004
1172
|
range: sourceRange,
|
|
1005
|
-
text:
|
|
1173
|
+
text: z2.string()
|
|
1006
1174
|
})),
|
|
1007
|
-
startedAt:
|
|
1008
|
-
requests:
|
|
1009
|
-
service:
|
|
1010
|
-
target:
|
|
1011
|
-
sequence:
|
|
1012
|
-
job:
|
|
1175
|
+
startedAt: z2.string().optional(),
|
|
1176
|
+
requests: z2.array(z2.object({
|
|
1177
|
+
service: z2.string(),
|
|
1178
|
+
target: z2.string(),
|
|
1179
|
+
sequence: z2.string(),
|
|
1180
|
+
job: z2.number().int().optional()
|
|
1013
1181
|
})),
|
|
1014
|
-
generated:
|
|
1182
|
+
generated: z2.literal(true).optional()
|
|
1015
1183
|
});
|
|
1016
|
-
var debugEventSite =
|
|
1017
|
-
id:
|
|
1018
|
-
kind:
|
|
1019
|
-
job:
|
|
1020
|
-
phase:
|
|
1021
|
-
path:
|
|
1022
|
-
function:
|
|
1023
|
-
type:
|
|
1184
|
+
var debugEventSite = z2.object({
|
|
1185
|
+
id: z2.number().int(),
|
|
1186
|
+
kind: z2.enum(["state", "decision", "job"]),
|
|
1187
|
+
job: z2.number().int().optional(),
|
|
1188
|
+
phase: z2.enum(["started", "finished"]).optional(),
|
|
1189
|
+
path: z2.string(),
|
|
1190
|
+
function: z2.number().int().optional(),
|
|
1191
|
+
type: z2.string(),
|
|
1024
1192
|
range: sourceRange,
|
|
1025
|
-
text:
|
|
1026
|
-
task:
|
|
1027
|
-
operands:
|
|
1028
|
-
text:
|
|
1193
|
+
text: z2.string(),
|
|
1194
|
+
task: z2.string(),
|
|
1195
|
+
operands: z2.array(z2.object({
|
|
1196
|
+
text: z2.string(),
|
|
1029
1197
|
range: sourceRange,
|
|
1030
|
-
type:
|
|
1031
|
-
path:
|
|
1032
|
-
stable:
|
|
1198
|
+
type: z2.string(),
|
|
1199
|
+
path: z2.string().optional(),
|
|
1200
|
+
stable: z2.boolean()
|
|
1033
1201
|
}))
|
|
1034
1202
|
});
|
|
1035
|
-
var debugWriter =
|
|
1036
|
-
output:
|
|
1037
|
-
target:
|
|
1038
|
-
sites:
|
|
1203
|
+
var debugWriter = z2.object({
|
|
1204
|
+
output: z2.string(),
|
|
1205
|
+
target: z2.string(),
|
|
1206
|
+
sites: z2.array(z2.object({
|
|
1207
|
+
id: z2.number().int(),
|
|
1208
|
+
range: sourceRange,
|
|
1209
|
+
function: z2.number().int()
|
|
1210
|
+
}))
|
|
1039
1211
|
});
|
|
1040
|
-
var debugTask =
|
|
1041
|
-
name:
|
|
1042
|
-
intervalMs:
|
|
1043
|
-
priority:
|
|
1212
|
+
var debugTask = z2.object({
|
|
1213
|
+
name: z2.string(),
|
|
1214
|
+
intervalMs: z2.number(),
|
|
1215
|
+
priority: z2.number().int()
|
|
1044
1216
|
});
|
|
1045
|
-
var debugManifest =
|
|
1046
|
-
kind:
|
|
1047
|
-
version:
|
|
1048
|
-
build:
|
|
1049
|
-
id:
|
|
1050
|
-
target:
|
|
1051
|
-
compiler:
|
|
1217
|
+
var debugManifest = z2.object({
|
|
1218
|
+
kind: z2.literal("tsplc-debug"),
|
|
1219
|
+
version: z2.literal(1),
|
|
1220
|
+
build: z2.object({
|
|
1221
|
+
id: z2.string(),
|
|
1222
|
+
target: z2.string(),
|
|
1223
|
+
compiler: z2.string()
|
|
1052
1224
|
}),
|
|
1053
|
-
sources:
|
|
1054
|
-
types:
|
|
1055
|
-
roots:
|
|
1056
|
-
constants:
|
|
1057
|
-
io:
|
|
1058
|
-
devices:
|
|
1059
|
-
functions:
|
|
1060
|
-
jobs:
|
|
1061
|
-
events:
|
|
1062
|
-
writers:
|
|
1063
|
-
tasks:
|
|
1064
|
-
errors:
|
|
1225
|
+
sources: z2.array(z2.string()),
|
|
1226
|
+
types: z2.record(z2.string(), debugType),
|
|
1227
|
+
roots: z2.array(debugRoot),
|
|
1228
|
+
constants: z2.array(debugConstant),
|
|
1229
|
+
io: z2.array(debugIo),
|
|
1230
|
+
devices: z2.array(debugDevice),
|
|
1231
|
+
functions: z2.array(debugFunction),
|
|
1232
|
+
jobs: z2.array(debugJob),
|
|
1233
|
+
events: z2.array(debugEventSite),
|
|
1234
|
+
writers: z2.array(debugWriter),
|
|
1235
|
+
tasks: z2.array(debugTask),
|
|
1236
|
+
errors: z2.record(z2.string(), z2.string())
|
|
1065
1237
|
});
|
|
1066
1238
|
|
|
1067
1239
|
// packages/debug/src/ui/index.ts
|
|
@@ -1254,10 +1426,12 @@ var build = async () => {
|
|
|
1254
1426
|
resolve(dirname2(createRequire(import.meta.url).resolve("@tsplc/debug")), "../src/ui/main.ts")
|
|
1255
1427
|
],
|
|
1256
1428
|
target: "browser",
|
|
1429
|
+
conditions: ["bun"],
|
|
1257
1430
|
minify: true
|
|
1258
1431
|
});
|
|
1259
|
-
if (!result.success)
|
|
1432
|
+
if (!result.success) {
|
|
1260
1433
|
throw new AggregateError(result.logs, "Bundling the debugger UI failed");
|
|
1434
|
+
}
|
|
1261
1435
|
return { html, script: await result.outputs[0].text() };
|
|
1262
1436
|
};
|
|
1263
1437
|
var loadUi = () => ui ??= build();
|
|
@@ -1270,8 +1444,9 @@ var list = (value) => (value ?? "").split(",").map((item) => item.trim()).filter
|
|
|
1270
1444
|
var loadManifest = async (file) => debugManifest.parse(JSON.parse(await readFile2(file, "utf8")));
|
|
1271
1445
|
var interest = (search) => {
|
|
1272
1446
|
const jobs = list(search.get("jobs")).map(Number);
|
|
1273
|
-
if (!jobs.every(Number.isInteger))
|
|
1447
|
+
if (!jobs.every(Number.isInteger)) {
|
|
1274
1448
|
throw new DebugError("jobs must be job ids", 400);
|
|
1449
|
+
}
|
|
1275
1450
|
return {
|
|
1276
1451
|
watch: list(search.get("watch")),
|
|
1277
1452
|
jobs,
|
|
@@ -1286,8 +1461,9 @@ var stream = (debug, { watch, ...shown }) => {
|
|
|
1286
1461
|
start: (controller) => {
|
|
1287
1462
|
let open = true;
|
|
1288
1463
|
const write = (text) => {
|
|
1289
|
-
if (open)
|
|
1464
|
+
if (open) {
|
|
1290
1465
|
controller.enqueue(encoder.encode(text));
|
|
1466
|
+
}
|
|
1291
1467
|
};
|
|
1292
1468
|
const send = ({ event, data }) => write(`event: ${event}
|
|
1293
1469
|
data: ${JSON.stringify(data)}
|
|
@@ -1307,12 +1483,18 @@ data: ${JSON.stringify(data)}
|
|
|
1307
1483
|
`), heartbeatMs);
|
|
1308
1484
|
const { intervalMs } = debug.session().reading;
|
|
1309
1485
|
const refresh = setInterval(() => {
|
|
1310
|
-
if (debug.session().connection.state !== "connected")
|
|
1486
|
+
if (debug.session().connection.state !== "connected") {
|
|
1311
1487
|
return;
|
|
1488
|
+
}
|
|
1312
1489
|
const now = Date.now();
|
|
1313
|
-
for (const { sample, sentAt } of latest.values())
|
|
1314
|
-
if (now - sentAt >= intervalMs)
|
|
1315
|
-
sendSample({
|
|
1490
|
+
for (const { sample, sentAt } of latest.values()) {
|
|
1491
|
+
if (now - sentAt >= intervalMs) {
|
|
1492
|
+
sendSample({
|
|
1493
|
+
...sample,
|
|
1494
|
+
receivedAt: new Date(now).toISOString()
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1316
1498
|
}, intervalMs);
|
|
1317
1499
|
stop = () => {
|
|
1318
1500
|
open = false;
|
|
@@ -1324,7 +1506,12 @@ data: ${JSON.stringify(data)}
|
|
|
1324
1506
|
};
|
|
1325
1507
|
},
|
|
1326
1508
|
cancel: () => stop()
|
|
1327
|
-
}), {
|
|
1509
|
+
}), {
|
|
1510
|
+
headers: {
|
|
1511
|
+
"Content-Type": "text/event-stream",
|
|
1512
|
+
"Cache-Control": "no-store"
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1328
1515
|
};
|
|
1329
1516
|
var createDebugHandler = (debug, log) => async (request, route) => {
|
|
1330
1517
|
const { searchParams } = new URL(request.url);
|
|
@@ -1360,12 +1547,15 @@ var createDebugHandler = (debug, log) => async (request, route) => {
|
|
|
1360
1547
|
}
|
|
1361
1548
|
return json2({ error: `No route ${key}` }, 404);
|
|
1362
1549
|
} catch (error) {
|
|
1363
|
-
if (error instanceof DebugError)
|
|
1550
|
+
if (error instanceof DebugError) {
|
|
1364
1551
|
return json2({ error: error.message }, error.status);
|
|
1365
|
-
|
|
1552
|
+
}
|
|
1553
|
+
if (error instanceof RangeError || error instanceof SyntaxError) {
|
|
1366
1554
|
return json2({ error: error.message }, 400);
|
|
1367
|
-
|
|
1368
|
-
|
|
1555
|
+
}
|
|
1556
|
+
if (error instanceof z3.ZodError) {
|
|
1557
|
+
return json2({ error: z3.prettifyError(error) }, 400);
|
|
1558
|
+
}
|
|
1369
1559
|
log(`${key} failed: ${error instanceof Error ? error.stack : String(error)}`);
|
|
1370
1560
|
return json2({ error: "The debugger failed; see its log" }, 500);
|
|
1371
1561
|
}
|
|
@@ -1377,8 +1567,9 @@ var serveDebugger = (debug, options) => {
|
|
|
1377
1567
|
port: options.port,
|
|
1378
1568
|
fetch: (request) => {
|
|
1379
1569
|
const host = request.headers.get("host")?.replace(/:\d+$/, "");
|
|
1380
|
-
if (host !== "127.0.0.1" && host !== "localhost")
|
|
1570
|
+
if (host !== "127.0.0.1" && host !== "localhost") {
|
|
1381
1571
|
return json2({ error: "Use http://127.0.0.1 or http://localhost" }, 403);
|
|
1572
|
+
}
|
|
1382
1573
|
return handle(request, new URL(request.url).pathname);
|
|
1383
1574
|
}
|
|
1384
1575
|
});
|
|
@@ -1386,19 +1577,15 @@ var serveDebugger = (debug, options) => {
|
|
|
1386
1577
|
// packages/debug/src/login.ts
|
|
1387
1578
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
1388
1579
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
var
|
|
1398
|
-
var integerTypes = new Set([
|
|
1399
|
-
...["SINT", "INT", "DINT", "LINT", "USINT", "UINT", "UDINT", "ULINT"],
|
|
1400
|
-
...["BYTE", "WORD", "DWORD", "LWORD"]
|
|
1401
|
-
]);
|
|
1580
|
+
// packages/debug/src/login-value.ts
|
|
1581
|
+
import { elementaryType } from "@tsplc/iec";
|
|
1582
|
+
var booleans = {
|
|
1583
|
+
TRUE: true,
|
|
1584
|
+
FALSE: false,
|
|
1585
|
+
"1": true,
|
|
1586
|
+
"0": false
|
|
1587
|
+
};
|
|
1588
|
+
var radixPrefixes = { 2: "0b", 8: "0o", 16: "0x" };
|
|
1402
1589
|
var timeUnits = {
|
|
1403
1590
|
d: 86400000,
|
|
1404
1591
|
h: 3600000,
|
|
@@ -1408,106 +1595,154 @@ var timeUnits = {
|
|
|
1408
1595
|
us: 0.001,
|
|
1409
1596
|
ns: 0.000001
|
|
1410
1597
|
};
|
|
1411
|
-
var
|
|
1412
|
-
|
|
1598
|
+
var stringEscapes = {
|
|
1599
|
+
l: `
|
|
1600
|
+
`,
|
|
1601
|
+
n: `
|
|
1602
|
+
`,
|
|
1603
|
+
p: "\f",
|
|
1604
|
+
r: "\r",
|
|
1605
|
+
t: "\t"
|
|
1606
|
+
};
|
|
1607
|
+
var withoutTypePrefix = (text) => text.replace(/^(?!L?T(?:IME)?#)[a-z_]+#(?=.)/i, "");
|
|
1608
|
+
var bigIntLiteral = (text) => {
|
|
1609
|
+
const [, radix, digits] = /^(2|8|16)#([0-9a-f_]+)$/i.exec(text) ?? [];
|
|
1610
|
+
if (radix !== undefined && digits !== undefined) {
|
|
1611
|
+
return `${radixPrefixes[radix]}${digits.replaceAll("_", "")}`;
|
|
1612
|
+
}
|
|
1613
|
+
const decimal = text.replaceAll("_", "");
|
|
1614
|
+
return /^[+-]?\d+$/.test(decimal) ? decimal : undefined;
|
|
1615
|
+
};
|
|
1413
1616
|
var integer = (text) => {
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1416
|
-
if (!radix && !/^[+-]?\d+$/.test(digits))
|
|
1617
|
+
const literal = bigIntLiteral(text);
|
|
1618
|
+
if (literal === undefined) {
|
|
1417
1619
|
return;
|
|
1418
|
-
|
|
1620
|
+
}
|
|
1621
|
+
const value = BigInt(literal);
|
|
1419
1622
|
return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
|
|
1420
1623
|
};
|
|
1624
|
+
var real = (text) => {
|
|
1625
|
+
const number = Number(text);
|
|
1626
|
+
return text !== "" && !Number.isNaN(number) ? number : undefined;
|
|
1627
|
+
};
|
|
1421
1628
|
var duration = (text) => {
|
|
1422
|
-
const body = /^L?T(?:IME)?#(-?)(.+)$/i.exec(text);
|
|
1423
|
-
if (
|
|
1629
|
+
const [, sign, body] = /^L?T(?:IME)?#(-?)(.+)$/i.exec(text) ?? [];
|
|
1630
|
+
if (body === undefined) {
|
|
1424
1631
|
return;
|
|
1425
|
-
|
|
1426
|
-
|
|
1632
|
+
}
|
|
1633
|
+
const units = body.replaceAll("_", "");
|
|
1634
|
+
const parts = [...units.matchAll(/(\d+(?:\.\d+)?)(ms|us|ns|d|h|m|s)/gi)];
|
|
1635
|
+
if (parts.map(([part]) => part).join("") !== units) {
|
|
1427
1636
|
return;
|
|
1637
|
+
}
|
|
1428
1638
|
const ms = parts.reduce((total, [, amount, unit]) => total + Number(amount) * timeUnits[unit.toLowerCase()], 0);
|
|
1429
|
-
return
|
|
1639
|
+
return sign ? -ms : ms;
|
|
1430
1640
|
};
|
|
1431
1641
|
var quoted = (text) => {
|
|
1432
|
-
const body = /^(['"])(.*)\1$/s.exec(text);
|
|
1433
|
-
|
|
1642
|
+
const [, , body] = /^(['"])(.*)\1$/s.exec(text) ?? [];
|
|
1643
|
+
return body?.replace(/\$([0-9a-f]{2}|[$'"lnprt])/gi, (_, code) => code.length === 2 ? String.fromCharCode(parseInt(code, 16)) : stringEscapes[code.toLowerCase()] ?? code);
|
|
1644
|
+
};
|
|
1645
|
+
var enumValue = (type, text) => {
|
|
1646
|
+
const number = integer(text);
|
|
1647
|
+
if (typeof number === "number") {
|
|
1648
|
+
return number;
|
|
1649
|
+
}
|
|
1650
|
+
const [, name, value] = /^(?:\w+\.)*(\w+)(?:\s*\((-?\d+)\))?$/.exec(text) ?? [];
|
|
1651
|
+
if (name === undefined) {
|
|
1434
1652
|
return;
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
return
|
|
1442
|
-
}
|
|
1653
|
+
}
|
|
1654
|
+
return type.members[name] ?? (value === undefined ? name : Number(value));
|
|
1655
|
+
};
|
|
1656
|
+
var arrayValue = (type, text) => {
|
|
1657
|
+
const items = text.replace(/^\[/, "").replace(/\]$/, "").trim();
|
|
1658
|
+
if (!items) {
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
const values = items.split(/\s*,\s*/).map((item) => scalarValue(type.element, item));
|
|
1662
|
+
return values.length === type.length && values.every((value) => value !== undefined) ? values : undefined;
|
|
1663
|
+
};
|
|
1664
|
+
var scalarValue = (iec, text) => {
|
|
1665
|
+
const untyped = withoutTypePrefix(text);
|
|
1666
|
+
const name = iec.toUpperCase();
|
|
1667
|
+
switch (elementaryType(name)?.family) {
|
|
1668
|
+
case "bool":
|
|
1669
|
+
return booleans[untyped.toUpperCase()];
|
|
1670
|
+
case "integer":
|
|
1671
|
+
case "bits":
|
|
1672
|
+
return integer(untyped);
|
|
1673
|
+
case "real":
|
|
1674
|
+
return real(untyped);
|
|
1675
|
+
case "duration":
|
|
1676
|
+
return duration(text);
|
|
1677
|
+
}
|
|
1678
|
+
if (/^W?STRING\b/.test(name)) {
|
|
1679
|
+
return quoted(text) ?? text;
|
|
1680
|
+
}
|
|
1681
|
+
return text;
|
|
1443
1682
|
};
|
|
1444
1683
|
var parseLoginValue = (type, text) => {
|
|
1445
1684
|
const trimmed = text.trim();
|
|
1446
|
-
const untyped = trimmed.replace(/^(?!L?T(?:IME)?#)[a-z_]+#(?=.)/i, "");
|
|
1447
1685
|
switch (type.kind) {
|
|
1448
|
-
case "enum":
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
const named = /^(?:\w+\.)*(\w+)(?:\s*\((-?\d+)\))?$/.exec(untyped);
|
|
1453
|
-
if (!named)
|
|
1454
|
-
return;
|
|
1455
|
-
return type.members[named[1]] ?? (named[2] === undefined ? named[1] : Number(named[2]));
|
|
1456
|
-
}
|
|
1457
|
-
case "array": {
|
|
1458
|
-
const items = /^\[?(.*?)\]?$/s.exec(untyped)[1].trim();
|
|
1459
|
-
if (!items)
|
|
1460
|
-
return [];
|
|
1461
|
-
const element = { kind: "scalar", iec: type.element };
|
|
1462
|
-
const values = items.split(/\s*,\s*/).map((item) => parseLoginValue(element, item));
|
|
1463
|
-
return values.length === type.length && values.every((value) => value !== undefined) ? values : undefined;
|
|
1464
|
-
}
|
|
1686
|
+
case "enum":
|
|
1687
|
+
return enumValue(type, withoutTypePrefix(trimmed));
|
|
1688
|
+
case "array":
|
|
1689
|
+
return arrayValue(type, withoutTypePrefix(trimmed));
|
|
1465
1690
|
case "record":
|
|
1466
1691
|
return;
|
|
1467
1692
|
case "scalar":
|
|
1468
|
-
|
|
1469
|
-
}
|
|
1470
|
-
const iec = type.iec.toUpperCase();
|
|
1471
|
-
if (iec === "BOOL")
|
|
1472
|
-
return { TRUE: true, FALSE: false, 1: true, 0: false }[untyped.toUpperCase()];
|
|
1473
|
-
if (integerTypes.has(iec))
|
|
1474
|
-
return integer(untyped);
|
|
1475
|
-
if (iec === "REAL" || iec === "LREAL") {
|
|
1476
|
-
const number = Number(untyped);
|
|
1477
|
-
return untyped !== "" && !Number.isNaN(number) ? number : undefined;
|
|
1693
|
+
return scalarValue(type.iec, trimmed);
|
|
1478
1694
|
}
|
|
1479
|
-
if (iec === "TIME" || iec === "LTIME")
|
|
1480
|
-
return duration(trimmed);
|
|
1481
|
-
if (/^W?STRING\b/.test(iec))
|
|
1482
|
-
return quoted(trimmed) ?? trimmed;
|
|
1483
|
-
return trimmed;
|
|
1484
1695
|
};
|
|
1696
|
+
|
|
1697
|
+
// packages/debug/src/login.ts
|
|
1698
|
+
class LoginUnavailable extends Error {
|
|
1699
|
+
constructor(message, options) {
|
|
1700
|
+
super(message, options);
|
|
1701
|
+
this.state = options?.state ?? "unavailable";
|
|
1702
|
+
}
|
|
1703
|
+
state;
|
|
1704
|
+
}
|
|
1705
|
+
var noLogin = "Live values need an engineering login: run `tsplc login`.";
|
|
1706
|
+
var retryMs = 5000;
|
|
1707
|
+
var typeName = (type) => type.kind === "scalar" ? type.iec : type.kind;
|
|
1485
1708
|
var createLoginAdapter = (options) => {
|
|
1486
1709
|
const samplingMs = options.samplingMs ?? 1000;
|
|
1487
|
-
let connection = {
|
|
1710
|
+
let connection = {
|
|
1711
|
+
state: "connecting",
|
|
1712
|
+
since: new Date().toISOString()
|
|
1713
|
+
};
|
|
1488
1714
|
const connectionListeners = new Set;
|
|
1489
1715
|
const subscriptions = new Set;
|
|
1490
1716
|
const rejected = new Map;
|
|
1491
1717
|
const controller = new AbortController;
|
|
1492
1718
|
const setConnection = (state, error) => {
|
|
1493
|
-
if (controller.signal.aborted)
|
|
1719
|
+
if (controller.signal.aborted) {
|
|
1494
1720
|
return;
|
|
1495
|
-
|
|
1721
|
+
}
|
|
1722
|
+
if (connection.state === state && connection.error === error) {
|
|
1496
1723
|
return;
|
|
1497
|
-
|
|
1724
|
+
}
|
|
1725
|
+
if (state !== "connected") {
|
|
1498
1726
|
rejected.clear();
|
|
1499
|
-
|
|
1500
|
-
|
|
1727
|
+
}
|
|
1728
|
+
connection = {
|
|
1729
|
+
state,
|
|
1730
|
+
since: new Date().toISOString(),
|
|
1731
|
+
...error && { error }
|
|
1732
|
+
};
|
|
1733
|
+
for (const listener of connectionListeners) {
|
|
1501
1734
|
listener(connection);
|
|
1735
|
+
}
|
|
1502
1736
|
};
|
|
1503
1737
|
const split = async (expressions) => {
|
|
1504
1738
|
try {
|
|
1505
1739
|
return await options.read(expressions);
|
|
1506
1740
|
} catch (error) {
|
|
1507
|
-
if (error instanceof LoginUnavailable)
|
|
1741
|
+
if (error instanceof LoginUnavailable) {
|
|
1508
1742
|
throw error;
|
|
1743
|
+
}
|
|
1509
1744
|
if (expressions.length === 1) {
|
|
1510
|
-
rejected.set(expressions[0],
|
|
1745
|
+
rejected.set(expressions[0], errorMessage(error));
|
|
1511
1746
|
return {};
|
|
1512
1747
|
}
|
|
1513
1748
|
const half = Math.ceil(expressions.length / 2);
|
|
@@ -1521,56 +1756,75 @@ var createLoginAdapter = (options) => {
|
|
|
1521
1756
|
try {
|
|
1522
1757
|
return await options.read(expressions);
|
|
1523
1758
|
} catch (error) {
|
|
1524
|
-
if (error instanceof LoginUnavailable)
|
|
1759
|
+
if (error instanceof LoginUnavailable) {
|
|
1525
1760
|
throw error;
|
|
1761
|
+
}
|
|
1526
1762
|
await options.read([options.probe]);
|
|
1527
1763
|
return split(expressions);
|
|
1528
1764
|
}
|
|
1529
1765
|
};
|
|
1530
|
-
const
|
|
1531
|
-
const unique = [...new Map(targets.map((target) => [target.target, target])).values()];
|
|
1532
|
-
const expressions = unique.map(({ target }) => target).filter((target) => !rejected.has(target));
|
|
1533
|
-
const samples = new Map;
|
|
1534
|
-
let values;
|
|
1766
|
+
const readTexts = async (expressions) => {
|
|
1535
1767
|
const overdue = setTimeout(() => setConnection("lost", "The engineering login stopped answering"), 3 * samplingMs);
|
|
1536
1768
|
try {
|
|
1537
|
-
|
|
1769
|
+
const texts = expressions.length ? await readText(expressions) : {};
|
|
1538
1770
|
setConnection("connected");
|
|
1771
|
+
return { texts };
|
|
1539
1772
|
} catch (error) {
|
|
1540
|
-
setConnection(error instanceof LoginUnavailable ? error.state : "lost",
|
|
1541
|
-
|
|
1542
|
-
for (const { target } of unique)
|
|
1543
|
-
samples.set(target, {
|
|
1544
|
-
value: null,
|
|
1545
|
-
quality: "unavailable",
|
|
1546
|
-
status: message2(error),
|
|
1547
|
-
receivedAt
|
|
1548
|
-
});
|
|
1549
|
-
return samples;
|
|
1773
|
+
setConnection(error instanceof LoginUnavailable ? error.state : "lost", errorMessage(error));
|
|
1774
|
+
return { error: errorMessage(error) };
|
|
1550
1775
|
} finally {
|
|
1551
1776
|
clearTimeout(overdue);
|
|
1552
1777
|
}
|
|
1778
|
+
};
|
|
1779
|
+
const parsedSample = ({ target, type }, text, receivedAt) => {
|
|
1780
|
+
const value = text === undefined ? undefined : parseLoginValue(type, text);
|
|
1781
|
+
if (value !== undefined) {
|
|
1782
|
+
return { value, quality: "good", status: "Good", receivedAt };
|
|
1783
|
+
}
|
|
1784
|
+
const reason = text === undefined ? "Not returned by the login" : `Not a ${typeName(type)}`;
|
|
1785
|
+
return {
|
|
1786
|
+
value: text ?? null,
|
|
1787
|
+
quality: "bad",
|
|
1788
|
+
status: rejected.get(target) ?? reason,
|
|
1789
|
+
receivedAt
|
|
1790
|
+
};
|
|
1791
|
+
};
|
|
1792
|
+
const read = async (targets) => {
|
|
1793
|
+
const unique = [
|
|
1794
|
+
...new Map(targets.map((target) => [target.target, target])).values()
|
|
1795
|
+
];
|
|
1796
|
+
const result = await readTexts(unique.map(({ target }) => target).filter((target) => !rejected.has(target)));
|
|
1553
1797
|
const receivedAt = new Date().toISOString();
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1798
|
+
if ("error" in result) {
|
|
1799
|
+
return new Map(unique.map(({ target }) => [
|
|
1800
|
+
target,
|
|
1801
|
+
{
|
|
1802
|
+
value: null,
|
|
1803
|
+
quality: "unavailable",
|
|
1804
|
+
status: result.error,
|
|
1805
|
+
receivedAt
|
|
1806
|
+
}
|
|
1807
|
+
]));
|
|
1808
|
+
}
|
|
1809
|
+
return new Map(unique.map((read) => [
|
|
1810
|
+
read.target,
|
|
1811
|
+
parsedSample(read, result.texts[read.target], receivedAt)
|
|
1812
|
+
]));
|
|
1813
|
+
};
|
|
1814
|
+
const pauseAfter = (tookMs) => {
|
|
1815
|
+
if (connection.state !== "connected" && connection.state !== "connecting") {
|
|
1816
|
+
return retryMs;
|
|
1564
1817
|
}
|
|
1565
|
-
return
|
|
1818
|
+
return tookMs > samplingMs ? tookMs : samplingMs - tookMs;
|
|
1566
1819
|
};
|
|
1567
1820
|
const poll = async () => {
|
|
1568
1821
|
while (!controller.signal.aborted) {
|
|
1569
1822
|
const started = Date.now();
|
|
1570
1823
|
const due = [...subscriptions].filter(({ due }) => due <= started + samplingMs / 2);
|
|
1571
1824
|
if (due.length) {
|
|
1572
|
-
for (const subscription of due)
|
|
1825
|
+
for (const subscription of due) {
|
|
1573
1826
|
subscription.due = started + subscription.intervalMs;
|
|
1827
|
+
}
|
|
1574
1828
|
const stopped = Promise.withResolvers();
|
|
1575
1829
|
const stop = () => stopped.resolve();
|
|
1576
1830
|
controller.signal.addEventListener("abort", stop, { once: true });
|
|
@@ -1578,16 +1832,18 @@ var createLoginAdapter = (options) => {
|
|
|
1578
1832
|
read(due.flatMap(({ targets }) => targets)),
|
|
1579
1833
|
stopped.promise
|
|
1580
1834
|
]).finally(() => controller.signal.removeEventListener("abort", stop));
|
|
1581
|
-
if (!samples || controller.signal.aborted)
|
|
1835
|
+
if (!samples || controller.signal.aborted) {
|
|
1582
1836
|
break;
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1837
|
+
}
|
|
1838
|
+
for (const subscription of due) {
|
|
1839
|
+
if (subscriptions.has(subscription)) {
|
|
1840
|
+
for (const { target } of subscription.targets) {
|
|
1586
1841
|
subscription.listener(target, samples.get(target));
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1587
1845
|
}
|
|
1588
|
-
|
|
1589
|
-
const waiting = connection.state !== "connected" && connection.state !== "connecting";
|
|
1590
|
-
await delay2(waiting ? retryMs : took > samplingMs ? took : samplingMs - took, undefined, {
|
|
1846
|
+
await delay2(pauseAfter(Date.now() - started), undefined, {
|
|
1591
1847
|
signal: controller.signal
|
|
1592
1848
|
}).catch(() => {
|
|
1593
1849
|
return;
|
|
@@ -1619,20 +1875,19 @@ var createLoginAdapter = (options) => {
|
|
|
1619
1875
|
};
|
|
1620
1876
|
};
|
|
1621
1877
|
// packages/debug/src/opcua.ts
|
|
1622
|
-
import {
|
|
1623
|
-
|
|
1878
|
+
import {
|
|
1879
|
+
createMonitor,
|
|
1880
|
+
monitorConnection
|
|
1881
|
+
} from "@tsplc/opcua";
|
|
1624
1882
|
var opcUaConnection = (monitor, options) => {
|
|
1625
|
-
const password = monitor.passwordEnv
|
|
1626
|
-
if (monitor.username && !password)
|
|
1883
|
+
const password = monitor.passwordEnv ? options.env[monitor.passwordEnv] : undefined;
|
|
1884
|
+
if (monitor.username && !password) {
|
|
1627
1885
|
throw new Error(monitor.passwordEnv ? `Set ${monitor.passwordEnv} to the OPC UA password of ${monitor.username}.` : "Configure monitor.passwordEnv for the OPC UA user.");
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
securityMode: monitor.securityMode === "Sign" ? "Sign" : "SignAndEncrypt",
|
|
1634
|
-
...monitor.username && password && { credentials: { username: monitor.username, password } }
|
|
1635
|
-
};
|
|
1886
|
+
}
|
|
1887
|
+
return monitorConnection(monitor, {
|
|
1888
|
+
root: options.root,
|
|
1889
|
+
password: password ?? null
|
|
1890
|
+
});
|
|
1636
1891
|
};
|
|
1637
1892
|
var createOpcUaAdapter = (options) => {
|
|
1638
1893
|
const monitor = createMonitor(options);
|
|
@@ -1651,10 +1906,7 @@ var createOpcUaAdapter = (options) => {
|
|
|
1651
1906
|
samplingMs: monitor.samplingMs,
|
|
1652
1907
|
connection: monitor.status,
|
|
1653
1908
|
onConnection: monitor.onStatus,
|
|
1654
|
-
read: async (targets) => new Map(Object.entries(await monitor.read(names(targets))).map(([target, reading]) => [
|
|
1655
|
-
target,
|
|
1656
|
-
sample(reading)
|
|
1657
|
-
])),
|
|
1909
|
+
read: async (targets) => new Map(Object.entries(await monitor.read(names(targets))).map(([target, reading]) => [target, sample(reading)])),
|
|
1658
1910
|
subscribe: (targets, listener) => monitor.watch(names(targets), (target, reading) => listener(target, sample(reading))),
|
|
1659
1911
|
close: async () => {
|
|
1660
1912
|
controller.abort();
|
|
@@ -1677,5 +1929,5 @@ export {
|
|
|
1677
1929
|
timelineEntries
|
|
1678
1930
|
};
|
|
1679
1931
|
|
|
1680
|
-
//# debugId=
|
|
1932
|
+
//# debugId=D1ADCD561935AA0C64756E2164756E21
|
|
1681
1933
|
//# sourceMappingURL=index.js.map
|