@vessel-dsp/core 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formats/interchange/parser.d.ts.map +1 -1
- package/dist/formats/interchange/parser.js +99 -10
- package/dist/formats/interchange/parser.js.map +1 -1
- package/dist/formats/interchange/serializer.js +23 -0
- package/dist/formats/interchange/serializer.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model/device-terminal-roles.d.ts.map +1 -1
- package/dist/model/device-terminal-roles.js +5 -1
- package/dist/model/device-terminal-roles.js.map +1 -1
- package/dist/model/types.d.ts +70 -3
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/formats/interchange/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,MAAM,EAIX,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EA0BX,eAAe,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/formats/interchange/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,MAAM,EAIX,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EA0BX,eAAe,EAwEf,MAAM,mBAAmB,CAAC;AAqC3B,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACrD,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kBAAkB,EAAE,UAAU,GAAG,WAAW,CAAC;CAC7C,CAAC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA+EpE;AAED,gFAAgF;AAChF,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,MAAM,GACZ,8BAA8B,CAkBhC"}
|
|
@@ -2145,6 +2145,7 @@ function parseComponents(value, warnings) {
|
|
|
2145
2145
|
...parseComponentDevices(component.devices, `${path}.devices`),
|
|
2146
2146
|
...parseComponentWindings(component.windings, `${path}.windings`),
|
|
2147
2147
|
...parseComponentProgram(component.program, `${path}.program`),
|
|
2148
|
+
...parseComponentController(component.controller, component.terminals, `${path}.controller`),
|
|
2148
2149
|
properties: parseProperties(component.properties, `${path}.properties`),
|
|
2149
2150
|
sourceTypeName,
|
|
2150
2151
|
};
|
|
@@ -2247,13 +2248,13 @@ function assertScannedRoutersNameRealComponents(components) {
|
|
|
2247
2248
|
// A scanned parameter makes the same claim about a chip on the board, and is held to it.
|
|
2248
2249
|
program.positions.forEach((position, index) => {
|
|
2249
2250
|
for (const [name, parameter] of Object.entries(position.parameters ?? {})) {
|
|
2250
|
-
if (parameter.read !== "scanned")
|
|
2251
|
+
if (parameter.read !== "scanned" && parameter.read !== "tapped")
|
|
2251
2252
|
continue;
|
|
2252
2253
|
check(parameter.scannedBy, `components.${component.id}.program.positions[${index}].parameters.${name}`);
|
|
2253
2254
|
}
|
|
2254
2255
|
for (const [line, parameters] of Object.entries(position.lines ?? {})) {
|
|
2255
2256
|
for (const [name, parameter] of Object.entries(parameters)) {
|
|
2256
|
-
if (parameter.read !== "scanned")
|
|
2257
|
+
if (parameter.read !== "scanned" && parameter.read !== "tapped")
|
|
2257
2258
|
continue;
|
|
2258
2259
|
check(parameter.scannedBy, `components.${component.id}.program.positions[${index}].lines.${line}.${name}`);
|
|
2259
2260
|
}
|
|
@@ -2271,22 +2272,25 @@ function assertScannedRoutersNameRealComponents(components) {
|
|
|
2271
2272
|
function parseScannedRead(value, path, what) {
|
|
2272
2273
|
const readRaw = value.read;
|
|
2273
2274
|
const read = readRaw === undefined ? "node" : expectString(readRaw, `${path}.read`).trim();
|
|
2274
|
-
|
|
2275
|
-
|
|
2275
|
+
const allowed = what === "parameter" ? ["node", "scanned", "tapped"] : ["node", "scanned"];
|
|
2276
|
+
if (!allowed.includes(read)) {
|
|
2277
|
+
throw new Error(`${path}.read: expected ${allowed.map((v) => JSON.stringify(v)).join(" or ")}, got ${JSON.stringify(read)}`);
|
|
2276
2278
|
}
|
|
2277
2279
|
const scannedByRaw = value.scannedBy;
|
|
2278
2280
|
const scannedBy = scannedByRaw === undefined
|
|
2279
2281
|
? undefined
|
|
2280
2282
|
: expectString(scannedByRaw, `${path}.scannedBy`).trim();
|
|
2281
|
-
if (read
|
|
2283
|
+
if (read !== "node" && (scannedBy === undefined || scannedBy.length === 0)) {
|
|
2282
2284
|
// The claim has to name a chip that exists, or `scanned` becomes a way to make any
|
|
2283
2285
|
// dangling control appear to work -- which is the opposite of what it is for.
|
|
2284
|
-
throw new Error(`${path}.scannedBy: a
|
|
2286
|
+
throw new Error(`${path}.scannedBy: a ${read} ${what} names the component that reads the control`);
|
|
2285
2287
|
}
|
|
2286
2288
|
if (read === "node" && scannedBy !== undefined) {
|
|
2287
2289
|
throw new Error(`${path}.scannedBy: only a scanned ${what} names a reader; this one reads a node`);
|
|
2288
2290
|
}
|
|
2289
|
-
return scannedBy === undefined
|
|
2291
|
+
return scannedBy === undefined
|
|
2292
|
+
? { read: read }
|
|
2293
|
+
: { read: read, scannedBy };
|
|
2290
2294
|
}
|
|
2291
2295
|
function parseProgramRouter(value, path) {
|
|
2292
2296
|
if (value === undefined)
|
|
@@ -2296,7 +2300,9 @@ function parseProgramRouter(value, path) {
|
|
|
2296
2300
|
if (control.length === 0) {
|
|
2297
2301
|
throw new Error(`${path}.control: a router names the control that selects the program`);
|
|
2298
2302
|
}
|
|
2299
|
-
const
|
|
2303
|
+
const parsedRead = parseScannedRead(router, path, "router");
|
|
2304
|
+
const read = parsedRead.read;
|
|
2305
|
+
const scannedBy = parsedRead.scannedBy;
|
|
2300
2306
|
const positions = expectNumber(router.positions, `${path}.positions`);
|
|
2301
2307
|
if (!Number.isInteger(positions) || positions < 2) {
|
|
2302
2308
|
throw new Error(`${path}.positions: a router's control has at least 2 discrete positions, got ${positions}`);
|
|
@@ -2345,13 +2351,24 @@ function parseProgramParameter(raw, path) {
|
|
|
2345
2351
|
? parameter.source.trim()
|
|
2346
2352
|
: undefined;
|
|
2347
2353
|
const reading = parseScannedRead(parameter, path, "parameter");
|
|
2348
|
-
if (reading.read
|
|
2349
|
-
throw new Error(`${path}.read: a
|
|
2354
|
+
if (reading.read !== "node" && control === undefined) {
|
|
2355
|
+
throw new Error(`${path}.read: a ${reading.read} parameter names the control it reads`);
|
|
2356
|
+
}
|
|
2357
|
+
let ratio;
|
|
2358
|
+
if (parameter.ratio !== undefined) {
|
|
2359
|
+
if (reading.read !== "tapped") {
|
|
2360
|
+
throw new Error(`${path}.ratio: only a tapped parameter scales its interval`);
|
|
2361
|
+
}
|
|
2362
|
+
ratio = expectNumber(parameter.ratio, `${path}.ratio`);
|
|
2363
|
+
if (!(Number.isFinite(ratio) && ratio > 0)) {
|
|
2364
|
+
throw new Error(`${path}.ratio: a tap ratio is a positive number, got ${ratio}`);
|
|
2365
|
+
}
|
|
2350
2366
|
}
|
|
2351
2367
|
return {
|
|
2352
2368
|
...(control === undefined ? {} : { control }),
|
|
2353
2369
|
...(parameter.read === undefined ? {} : { read: reading.read }),
|
|
2354
2370
|
...(reading.scannedBy === undefined ? {} : { scannedBy: reading.scannedBy }),
|
|
2371
|
+
...(ratio === undefined ? {} : { ratio }),
|
|
2355
2372
|
min: expectNumber(parameter.min, `${path}.min`),
|
|
2356
2373
|
max: expectNumber(parameter.max, `${path}.max`),
|
|
2357
2374
|
...(source === undefined ? {} : { source }),
|
|
@@ -2434,6 +2451,78 @@ function parseComponentProgram(value, path) {
|
|
|
2434
2451
|
},
|
|
2435
2452
|
};
|
|
2436
2453
|
}
|
|
2454
|
+
/**
|
|
2455
|
+
* A microcontroller's declared firmware rule, checked against the component it sits on.
|
|
2456
|
+
*
|
|
2457
|
+
* Refuses rather than repairs: a latch nothing toggles, a pin following a latch that does not
|
|
2458
|
+
* exist, or a pin on a terminal the chip does not have is a declaration no consumer can execute,
|
|
2459
|
+
* and a document that silently loses half of one is the defect this format exists to prevent.
|
|
2460
|
+
*/
|
|
2461
|
+
function parseComponentController(value, rawTerminals, path) {
|
|
2462
|
+
if (value === undefined)
|
|
2463
|
+
return {};
|
|
2464
|
+
const controller = expectObject(value, path);
|
|
2465
|
+
const latches = optionalArray(controller.latches, `${path}.latches`).map((item, index) => {
|
|
2466
|
+
const latchPath = `${path}.latches[${index}]`;
|
|
2467
|
+
const latch = expectObject(item, latchPath);
|
|
2468
|
+
const initial = expectNumber(latch.initial, `${latchPath}.initial`);
|
|
2469
|
+
if (initial !== 0 && initial !== 1) {
|
|
2470
|
+
throw new Error(`${latchPath}.initial: a latch starts at 0 or 1, got ${initial}`);
|
|
2471
|
+
}
|
|
2472
|
+
return {
|
|
2473
|
+
id: expectString(latch.id, `${latchPath}.id`),
|
|
2474
|
+
toggledBy: expectString(latch.toggledBy, `${latchPath}.toggledBy`),
|
|
2475
|
+
initial,
|
|
2476
|
+
source: expectCitation(latch.source, `${latchPath}.source`),
|
|
2477
|
+
};
|
|
2478
|
+
});
|
|
2479
|
+
if (latches.length === 0) {
|
|
2480
|
+
throw new Error(`${path}.latches: a controller declares at least one latch`);
|
|
2481
|
+
}
|
|
2482
|
+
const latchIds = new Set();
|
|
2483
|
+
for (const latch of latches) {
|
|
2484
|
+
if (latchIds.has(latch.id)) {
|
|
2485
|
+
throw new Error(`${path}.latches: latch "${latch.id}" is declared twice`);
|
|
2486
|
+
}
|
|
2487
|
+
latchIds.add(latch.id);
|
|
2488
|
+
}
|
|
2489
|
+
const terminals = optionalArray(rawTerminals, `${path}.terminals`).map((item) => expectObject(item, `${path}.terminals`));
|
|
2490
|
+
const terminalNames = new Set(terminals.map((terminal) => terminal.name));
|
|
2491
|
+
const pins = optionalArray(controller.pins, `${path}.pins`).map((item, index) => {
|
|
2492
|
+
const pinPath = `${path}.pins[${index}]`;
|
|
2493
|
+
const pin = expectObject(item, pinPath);
|
|
2494
|
+
const terminal = expectString(pin.terminal, `${pinPath}.terminal`);
|
|
2495
|
+
if (!terminalNames.has(terminal)) {
|
|
2496
|
+
throw new Error(`${pinPath}.terminal: "${terminal}" is not a terminal of this component`);
|
|
2497
|
+
}
|
|
2498
|
+
const follows = expectString(pin.follows, `${pinPath}.follows`);
|
|
2499
|
+
if (!latchIds.has(follows)) {
|
|
2500
|
+
throw new Error(`${pinPath}.follows: "${follows}" is not a latch this controller declares`);
|
|
2501
|
+
}
|
|
2502
|
+
return {
|
|
2503
|
+
terminal,
|
|
2504
|
+
follows,
|
|
2505
|
+
...(pin.invert === undefined ? {} : { invert: expectBoolean(pin.invert, `${pinPath}.invert`) }),
|
|
2506
|
+
source: expectCitation(pin.source, `${pinPath}.source`),
|
|
2507
|
+
};
|
|
2508
|
+
});
|
|
2509
|
+
if (pins.length > 0) {
|
|
2510
|
+
const roles = new Set(terminals.map((terminal) => terminal.role));
|
|
2511
|
+
for (const role of ["supplyPositive", "supplyNegative"]) {
|
|
2512
|
+
if (!roles.has(role)) {
|
|
2513
|
+
throw new Error(`${path}.pins: a controller that drives pins needs its component to declare a ${role} terminal, which sets the pin levels`);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
return { controller: { latches, ...(pins.length === 0 ? {} : { pins }) } };
|
|
2518
|
+
}
|
|
2519
|
+
function expectCitation(value, path) {
|
|
2520
|
+
const text = expectString(value, path).trim();
|
|
2521
|
+
if (text.length === 0) {
|
|
2522
|
+
throw new Error(`${path}: a firmware rule is only as good as its source; cite one`);
|
|
2523
|
+
}
|
|
2524
|
+
return text;
|
|
2525
|
+
}
|
|
2437
2526
|
/**
|
|
2438
2527
|
* A transformer's declared winding list, or nothing when it declares none.
|
|
2439
2528
|
*
|