@vessel-dsp/core 0.5.0 → 0.6.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/README.md +10 -0
- package/package.json +1 -1
- package/src/formats/document.ts +145 -3
- package/src/formats/interchange/parser.ts +667 -9
- package/src/formats/interchange/serializer.ts +96 -1
- package/src/index.ts +51 -1
- package/src/model/types.ts +302 -1
- package/src/model/validation.ts +579 -1
|
@@ -3,8 +3,32 @@ import type {
|
|
|
3
3
|
CircuitDocument,
|
|
4
4
|
CircuitDocumentDevice,
|
|
5
5
|
CircuitDocumentDeviceKind,
|
|
6
|
+
BoardApplicability,
|
|
7
|
+
BoardEdgeTerminal,
|
|
8
|
+
BoardFamily,
|
|
9
|
+
BoardFootprint,
|
|
10
|
+
BoardFootprintCatalog,
|
|
11
|
+
BoardFootprintPlacement,
|
|
12
|
+
BoardKind,
|
|
13
|
+
BoardNet,
|
|
14
|
+
BoardNetMember,
|
|
15
|
+
BoardNetlist,
|
|
16
|
+
BoardPlacedPad,
|
|
17
|
+
BoardRealization,
|
|
18
|
+
BoardRoute,
|
|
19
|
+
BoardSourceCircuitHash,
|
|
20
|
+
BoardSubtype,
|
|
21
|
+
BuildBom,
|
|
22
|
+
BuildBomItem,
|
|
23
|
+
BuildBomRef,
|
|
24
|
+
BuildCompleteness,
|
|
25
|
+
BuildIntent,
|
|
26
|
+
BuildPartProfile,
|
|
27
|
+
BuildPartProfileCatalog,
|
|
28
|
+
BuildScope,
|
|
6
29
|
Component,
|
|
7
30
|
ComponentKind,
|
|
31
|
+
ComponentTerminalRef,
|
|
8
32
|
ControlApplicabilityPredicate,
|
|
9
33
|
ControlContext,
|
|
10
34
|
ControlGroup,
|
|
@@ -22,16 +46,27 @@ import type {
|
|
|
22
46
|
PanelColumnOrder,
|
|
23
47
|
PanelControlKind,
|
|
24
48
|
PanelElementBinding,
|
|
49
|
+
PanelElementPhysicalPlacement,
|
|
50
|
+
PanelFaceGeometry,
|
|
25
51
|
PanelGridLayout,
|
|
26
52
|
PanelGridIndexing,
|
|
27
53
|
PanelGridPosition,
|
|
28
54
|
PanelPlacementMetadata,
|
|
29
55
|
PanelRowOrder,
|
|
56
|
+
MechanicalBuildMetadata,
|
|
57
|
+
OffBoardWiringConnection,
|
|
58
|
+
OffBoardWiringCoverage,
|
|
59
|
+
OffBoardWiringEndpoint,
|
|
60
|
+
OffBoardWiringHarness,
|
|
61
|
+
OffBoardWiringHarnessStatus,
|
|
62
|
+
OffBoardWiringPlan,
|
|
30
63
|
ParsedQuantity,
|
|
31
64
|
Point,
|
|
32
65
|
PropertyValue,
|
|
33
66
|
Rotation,
|
|
34
67
|
Terminal,
|
|
68
|
+
VdspBuildDataObject,
|
|
69
|
+
VdspBuildDataValue,
|
|
35
70
|
Warning,
|
|
36
71
|
Wire,
|
|
37
72
|
} from '../../model/types';
|
|
@@ -55,23 +90,44 @@ type ParsedPair = Readonly<{
|
|
|
55
90
|
rest: string;
|
|
56
91
|
}>;
|
|
57
92
|
|
|
58
|
-
const
|
|
93
|
+
const INTERCHANGE_SCHEMA_V2 = 'circuit-interchange/v2';
|
|
94
|
+
const INTERCHANGE_SCHEMA_V3 = 'circuit-interchange/v3';
|
|
95
|
+
const V3_ONLY_TOP_LEVEL_FIELDS = [
|
|
96
|
+
'mechanical',
|
|
97
|
+
'build',
|
|
98
|
+
'bom',
|
|
99
|
+
'partProfiles',
|
|
100
|
+
'footprints',
|
|
101
|
+
'offBoardWiring',
|
|
102
|
+
'boards',
|
|
103
|
+
] as const;
|
|
59
104
|
|
|
60
105
|
export function parseInterchangeYaml(source: string): CircuitDocument {
|
|
61
106
|
const value = parseYamlSubset(source);
|
|
62
107
|
const root = expectObject(value, 'root');
|
|
63
108
|
const schema = expectString(root.schema, 'schema');
|
|
64
|
-
if (schema !==
|
|
109
|
+
if (schema !== INTERCHANGE_SCHEMA_V2 && schema !== INTERCHANGE_SCHEMA_V3) {
|
|
65
110
|
throw new Error(`unsupported interchange schema: ${schema}`);
|
|
66
111
|
}
|
|
112
|
+
const isV3 = schema === INTERCHANGE_SCHEMA_V3;
|
|
113
|
+
if (!isV3) {
|
|
114
|
+
rejectV3OnlyTopLevelFields(root);
|
|
115
|
+
}
|
|
67
116
|
|
|
68
|
-
const panel = parsePanel(root.panel);
|
|
117
|
+
const panel = parsePanel(root.panel, isV3);
|
|
69
118
|
const controlInterfaces = parseControlInterfaces(root.controlInterfaces);
|
|
70
119
|
const device = parseDevice(root.device);
|
|
71
120
|
const controlOutputs = parseControlOutputs(root.controlOutputs);
|
|
72
121
|
const controlGroups = parseControlGroups(root.controlGroups);
|
|
73
122
|
const controlContexts = parseControlContexts(root.controlContexts);
|
|
74
123
|
const deviceInterface = parseDeviceInterface(root.deviceInterface);
|
|
124
|
+
const mechanical = isV3 ? parseMechanical(root.mechanical) : undefined;
|
|
125
|
+
const build = isV3 ? parseBuild(root.build) : undefined;
|
|
126
|
+
const bom = isV3 ? parseBom(root.bom) : undefined;
|
|
127
|
+
const partProfiles = isV3 ? parsePartProfiles(root.partProfiles) : undefined;
|
|
128
|
+
const footprints = isV3 ? parseFootprints(root.footprints) : undefined;
|
|
129
|
+
const offBoardWiring = isV3 ? parseOffBoardWiring(root.offBoardWiring) : undefined;
|
|
130
|
+
const boards = isV3 ? parseBoards(root.boards) : undefined;
|
|
75
131
|
|
|
76
132
|
return {
|
|
77
133
|
metadata: parseMetadata(root.metadata),
|
|
@@ -79,6 +135,13 @@ export function parseInterchangeYaml(source: string): CircuitDocument {
|
|
|
79
135
|
...(device === undefined ? {} : { device }),
|
|
80
136
|
...(controlGroups === undefined ? {} : { controlGroups }),
|
|
81
137
|
...(controlContexts === undefined ? {} : { controlContexts }),
|
|
138
|
+
...(mechanical === undefined ? {} : { mechanical }),
|
|
139
|
+
...(build === undefined ? {} : { build }),
|
|
140
|
+
...(bom === undefined ? {} : { bom }),
|
|
141
|
+
...(partProfiles === undefined ? {} : { partProfiles }),
|
|
142
|
+
...(footprints === undefined ? {} : { footprints }),
|
|
143
|
+
...(offBoardWiring === undefined ? {} : { offBoardWiring }),
|
|
144
|
+
...(boards === undefined ? {} : { boards }),
|
|
82
145
|
...(deviceInterface === undefined ? {} : { deviceInterface }),
|
|
83
146
|
...(panel === undefined ? {} : { panel }),
|
|
84
147
|
...(controlInterfaces === undefined ? {} : { controlInterfaces }),
|
|
@@ -91,6 +154,525 @@ export function parseInterchangeYaml(source: string): CircuitDocument {
|
|
|
91
154
|
};
|
|
92
155
|
}
|
|
93
156
|
|
|
157
|
+
function rejectV3OnlyTopLevelFields(root: YamlObject): void {
|
|
158
|
+
for (const field of V3_ONLY_TOP_LEVEL_FIELDS) {
|
|
159
|
+
if (root[field] !== undefined) {
|
|
160
|
+
throw new Error(`${field}: requires schema ${INTERCHANGE_SCHEMA_V3}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function parseMechanical(value: YamlValue | undefined): MechanicalBuildMetadata | undefined {
|
|
166
|
+
if (value === undefined) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
const mechanical = expectObject(value, 'mechanical');
|
|
170
|
+
return {
|
|
171
|
+
...parseBuildDataObject(mechanical, 'mechanical'),
|
|
172
|
+
...(mechanical.schema === undefined ? {} : { schema: expectString(mechanical.schema, 'mechanical.schema') }),
|
|
173
|
+
...(mechanical.units === undefined ? {} : { units: expectString(mechanical.units, 'mechanical.units') }),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function parseBuild(value: YamlValue | undefined): BuildScope | undefined {
|
|
178
|
+
if (value === undefined) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
const build = expectObject(value, 'build');
|
|
182
|
+
return {
|
|
183
|
+
...parseBuildDataObject(build, 'build'),
|
|
184
|
+
schema: parseLiteralString(build.schema, 'build.schema', 'build-scope/v1'),
|
|
185
|
+
...(build.intent === undefined ? {} : { intent: parseBuildIntent(build.intent, 'build.intent') }),
|
|
186
|
+
...(build.completeness === undefined
|
|
187
|
+
? {}
|
|
188
|
+
: { completeness: parseBuildCompleteness(build.completeness, 'build.completeness') }),
|
|
189
|
+
...(build.selectedBoardId === undefined
|
|
190
|
+
? {}
|
|
191
|
+
: { selectedBoardId: expectString(build.selectedBoardId, 'build.selectedBoardId') }),
|
|
192
|
+
...(build.selectedOffBoardWiringHarnessIds === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: {
|
|
195
|
+
selectedOffBoardWiringHarnessIds: parseOptionalStringArray(
|
|
196
|
+
build.selectedOffBoardWiringHarnessIds,
|
|
197
|
+
'build.selectedOffBoardWiringHarnessIds',
|
|
198
|
+
) ?? [],
|
|
199
|
+
}),
|
|
200
|
+
...(build.alternateBoardIds === undefined
|
|
201
|
+
? {}
|
|
202
|
+
: { alternateBoardIds: parseOptionalStringArray(build.alternateBoardIds, 'build.alternateBoardIds') ?? [] }),
|
|
203
|
+
...(build.bomScope === undefined ? {} : { bomScope: expectString(build.bomScope, 'build.bomScope') }),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function parseBuildIntent(value: YamlValue | undefined, path: string): BuildIntent {
|
|
208
|
+
const intent = expectString(value, path);
|
|
209
|
+
if (intent === 'diy-build-artifact' || intent === 'schema-review-sample') {
|
|
210
|
+
return intent;
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`${path}: expected diy-build-artifact or schema-review-sample`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function parseBuildCompleteness(value: YamlValue | undefined, path: string): BuildCompleteness {
|
|
216
|
+
const completeness = expectString(value, path);
|
|
217
|
+
if (completeness === 'complete-selected-build' || completeness === 'partial-offboard-wiring') {
|
|
218
|
+
return completeness;
|
|
219
|
+
}
|
|
220
|
+
throw new Error(`${path}: expected complete-selected-build or partial-offboard-wiring`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function parseBom(value: YamlValue | undefined): BuildBom | undefined {
|
|
224
|
+
if (value === undefined) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
const bom = expectObject(value, 'bom');
|
|
228
|
+
return {
|
|
229
|
+
...parseBuildDataObject(bom, 'bom'),
|
|
230
|
+
schema: parseLiteralString(bom.schema, 'bom.schema', 'build-bom/v1'),
|
|
231
|
+
items: optionalArray(bom.items, 'bom.items').map(parseBomItem),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function parseBomItem(value: YamlValue, index: number): BuildBomItem {
|
|
236
|
+
const path = `bom.items[${index}]`;
|
|
237
|
+
const item = expectObject(value, path);
|
|
238
|
+
return {
|
|
239
|
+
...parseBuildDataObject(item, path),
|
|
240
|
+
id: expectString(item.id, `${path}.id`),
|
|
241
|
+
refs: optionalArray(item.refs, `${path}.refs`).map((ref, refIndex) =>
|
|
242
|
+
parseBomRef(ref, `${path}.refs[${refIndex}]`)
|
|
243
|
+
),
|
|
244
|
+
quantity: expectNumber(item.quantity, `${path}.quantity`),
|
|
245
|
+
...(item.value === undefined ? {} : { value: expectString(item.value, `${path}.value`) }),
|
|
246
|
+
...(item.partProfileId === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: { partProfileId: expectString(item.partProfileId, `${path}.partProfileId`) }),
|
|
249
|
+
...(item.category === undefined ? {} : { category: expectString(item.category, `${path}.category`) }),
|
|
250
|
+
...(item.sku === undefined ? {} : { sku: expectString(item.sku, `${path}.sku`) }),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function parseBomRef(value: YamlValue, path: string): BuildBomRef {
|
|
255
|
+
const ref = expectObject(value, path);
|
|
256
|
+
const kind = expectString(ref.kind, `${path}.kind`);
|
|
257
|
+
switch (kind) {
|
|
258
|
+
case 'component':
|
|
259
|
+
case 'device-interface-control':
|
|
260
|
+
case 'panel-element':
|
|
261
|
+
case 'board':
|
|
262
|
+
case 'freeform-build-item':
|
|
263
|
+
return {
|
|
264
|
+
...parseBuildDataObject(ref, path),
|
|
265
|
+
kind,
|
|
266
|
+
...(ref.componentId === undefined ? {} : { componentId: expectString(ref.componentId, `${path}.componentId`) }),
|
|
267
|
+
...(ref.controlId === undefined ? {} : { controlId: expectString(ref.controlId, `${path}.controlId`) }),
|
|
268
|
+
...(ref.panelElementId === undefined
|
|
269
|
+
? {}
|
|
270
|
+
: { panelElementId: expectString(ref.panelElementId, `${path}.panelElementId`) }),
|
|
271
|
+
...(ref.boardId === undefined ? {} : { boardId: expectString(ref.boardId, `${path}.boardId`) }),
|
|
272
|
+
...(ref.label === undefined ? {} : { label: expectString(ref.label, `${path}.label`) }),
|
|
273
|
+
};
|
|
274
|
+
default:
|
|
275
|
+
throw new Error(`${path}.kind: expected component, device-interface-control, panel-element, board, or freeform-build-item`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function parsePartProfiles(value: YamlValue | undefined): BuildPartProfileCatalog | undefined {
|
|
280
|
+
if (value === undefined) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
const catalog = expectObject(value, 'partProfiles');
|
|
284
|
+
return {
|
|
285
|
+
...parseBuildDataObject(catalog, 'partProfiles'),
|
|
286
|
+
schema: parseLiteralString(catalog.schema, 'partProfiles.schema', 'part-profile-catalog/v1'),
|
|
287
|
+
...(catalog.resolution === undefined
|
|
288
|
+
? {}
|
|
289
|
+
: { resolution: expectString(catalog.resolution, 'partProfiles.resolution') }),
|
|
290
|
+
...(catalog.units === undefined ? {} : { units: expectString(catalog.units, 'partProfiles.units') }),
|
|
291
|
+
profiles: optionalArray(catalog.profiles, 'partProfiles.profiles').map((profile, index) =>
|
|
292
|
+
parsePartProfile(profile, index)
|
|
293
|
+
),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function parsePartProfile(value: YamlValue, index: number): BuildPartProfile {
|
|
298
|
+
const path = `partProfiles.profiles[${index}]`;
|
|
299
|
+
const profile = expectObject(value, path);
|
|
300
|
+
return {
|
|
301
|
+
...parseBuildDataObject(profile, path),
|
|
302
|
+
id: expectString(profile.id, `${path}.id`),
|
|
303
|
+
...(profile.kind === undefined ? {} : { kind: expectString(profile.kind, `${path}.kind`) }),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function parseFootprints(value: YamlValue | undefined): BoardFootprintCatalog | undefined {
|
|
308
|
+
if (value === undefined) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
const catalog = expectObject(value, 'footprints');
|
|
312
|
+
return {
|
|
313
|
+
...parseBuildDataObject(catalog, 'footprints'),
|
|
314
|
+
schema: parseLiteralString(catalog.schema, 'footprints.schema', 'board-footprint-catalog/v1'),
|
|
315
|
+
...(catalog.resolution === undefined ? {} : { resolution: expectString(catalog.resolution, 'footprints.resolution') }),
|
|
316
|
+
...(catalog.units === undefined ? {} : { units: expectString(catalog.units, 'footprints.units') }),
|
|
317
|
+
footprints: optionalArray(catalog.footprints, 'footprints.footprints').map((footprint, index) =>
|
|
318
|
+
parseFootprint(footprint, index)
|
|
319
|
+
),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function parseFootprint(value: YamlValue, index: number): BoardFootprint {
|
|
324
|
+
const path = `footprints.footprints[${index}]`;
|
|
325
|
+
const footprint = expectObject(value, path);
|
|
326
|
+
return {
|
|
327
|
+
...parseBuildDataObject(footprint, path),
|
|
328
|
+
id: expectString(footprint.id, `${path}.id`),
|
|
329
|
+
...(footprint.boardApplicability === undefined
|
|
330
|
+
? {}
|
|
331
|
+
: { boardApplicability: parseBoardApplicability(footprint.boardApplicability, `${path}.boardApplicability`) }),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function parseBoardApplicability(value: YamlValue, path: string): BoardApplicability {
|
|
336
|
+
const applicability = expectObject(value, path);
|
|
337
|
+
return {
|
|
338
|
+
...parseBuildDataObject(applicability, path),
|
|
339
|
+
family: parseBoardFamily(applicability.family, `${path}.family`),
|
|
340
|
+
kind: parseBoardKind(applicability.kind, `${path}.kind`),
|
|
341
|
+
...(applicability.subtype === undefined
|
|
342
|
+
? {}
|
|
343
|
+
: { subtype: parseBoardSubtype(applicability.subtype, `${path}.subtype`) }),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function parseOffBoardWiring(value: YamlValue | undefined): OffBoardWiringPlan | undefined {
|
|
348
|
+
if (value === undefined) {
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
const plan = expectObject(value, 'offBoardWiring');
|
|
352
|
+
return {
|
|
353
|
+
...parseBuildDataObject(plan, 'offBoardWiring'),
|
|
354
|
+
schema: parseLiteralString(plan.schema, 'offBoardWiring.schema', 'offboard-wiring/v1'),
|
|
355
|
+
...(plan.source === undefined ? {} : { source: expectString(plan.source, 'offBoardWiring.source') }),
|
|
356
|
+
...(plan.coverage === undefined
|
|
357
|
+
? {}
|
|
358
|
+
: { coverage: parseOffBoardWiringCoverage(plan.coverage, 'offBoardWiring.coverage') }),
|
|
359
|
+
harnesses: optionalArray(plan.harnesses, 'offBoardWiring.harnesses').map(parseOffBoardWiringHarness),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function parseOffBoardWiringCoverage(value: YamlValue | undefined, path: string): OffBoardWiringCoverage {
|
|
364
|
+
const coverage = expectString(value, path);
|
|
365
|
+
if (coverage === 'selected-build-complete' || coverage === 'representative-selected-build-endpoints') {
|
|
366
|
+
return coverage;
|
|
367
|
+
}
|
|
368
|
+
throw new Error(`${path}: expected selected-build-complete or representative-selected-build-endpoints`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function parseOffBoardWiringHarness(value: YamlValue, index: number): OffBoardWiringHarness {
|
|
372
|
+
const path = `offBoardWiring.harnesses[${index}]`;
|
|
373
|
+
const harness = expectObject(value, path);
|
|
374
|
+
return {
|
|
375
|
+
...parseBuildDataObject(harness, path),
|
|
376
|
+
id: expectString(harness.id, `${path}.id`),
|
|
377
|
+
...(harness.status === undefined
|
|
378
|
+
? {}
|
|
379
|
+
: { status: parseOffBoardWiringHarnessStatus(harness.status, `${path}.status`) }),
|
|
380
|
+
...(harness.notes === undefined ? {} : { notes: expectString(harness.notes, `${path}.notes`) }),
|
|
381
|
+
endpoints: optionalArray(harness.endpoints, `${path}.endpoints`).map((endpoint, endpointIndex) =>
|
|
382
|
+
parseOffBoardWiringEndpoint(endpoint, `${path}.endpoints[${endpointIndex}]`)
|
|
383
|
+
),
|
|
384
|
+
connections: optionalArray(harness.connections, `${path}.connections`).map((connection, connectionIndex) =>
|
|
385
|
+
parseOffBoardWiringConnection(connection, `${path}.connections[${connectionIndex}]`)
|
|
386
|
+
),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function parseOffBoardWiringHarnessStatus(value: YamlValue | undefined, path: string): OffBoardWiringHarnessStatus {
|
|
391
|
+
const status = expectString(value, path);
|
|
392
|
+
if (status === 'complete' || status === 'partial' || status === 'candidate') {
|
|
393
|
+
return status;
|
|
394
|
+
}
|
|
395
|
+
throw new Error(`${path}: expected complete, partial, or candidate`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function parseOffBoardWiringEndpoint(value: YamlValue, path: string): OffBoardWiringEndpoint {
|
|
399
|
+
const endpoint = expectObject(value, path);
|
|
400
|
+
const kind = expectString(endpoint.kind, `${path}.kind`);
|
|
401
|
+
switch (kind) {
|
|
402
|
+
case 'panel-component-terminal':
|
|
403
|
+
case 'board-terminal':
|
|
404
|
+
case 'power-terminal':
|
|
405
|
+
case 'footswitch-terminal':
|
|
406
|
+
case 'free-wire-label':
|
|
407
|
+
return {
|
|
408
|
+
...parseBuildDataObject(endpoint, path),
|
|
409
|
+
id: expectString(endpoint.id, `${path}.id`),
|
|
410
|
+
kind,
|
|
411
|
+
...(endpoint.componentId === undefined
|
|
412
|
+
? {}
|
|
413
|
+
: { componentId: expectString(endpoint.componentId, `${path}.componentId`) }),
|
|
414
|
+
...(endpoint.terminalName === undefined
|
|
415
|
+
? {}
|
|
416
|
+
: { terminalName: expectString(endpoint.terminalName, `${path}.terminalName`) }),
|
|
417
|
+
...(endpoint.panelElementId === undefined
|
|
418
|
+
? {}
|
|
419
|
+
: { panelElementId: expectString(endpoint.panelElementId, `${path}.panelElementId`) }),
|
|
420
|
+
...(endpoint.boardId === undefined ? {} : { boardId: expectString(endpoint.boardId, `${path}.boardId`) }),
|
|
421
|
+
...(endpoint.terminalId === undefined
|
|
422
|
+
? {}
|
|
423
|
+
: { terminalId: expectString(endpoint.terminalId, `${path}.terminalId`) }),
|
|
424
|
+
...(endpoint.label === undefined ? {} : { label: expectString(endpoint.label, `${path}.label`) }),
|
|
425
|
+
};
|
|
426
|
+
default:
|
|
427
|
+
throw new Error(`${path}.kind: expected a supported off-board wiring endpoint kind`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function parseOffBoardWiringConnection(value: YamlValue, path: string): OffBoardWiringConnection {
|
|
432
|
+
const connection = expectObject(value, path);
|
|
433
|
+
return {
|
|
434
|
+
...parseBuildDataObject(connection, path),
|
|
435
|
+
id: expectString(connection.id, `${path}.id`),
|
|
436
|
+
fromEndpointId: expectString(connection.fromEndpointId, `${path}.fromEndpointId`),
|
|
437
|
+
toEndpointId: expectString(connection.toEndpointId, `${path}.toEndpointId`),
|
|
438
|
+
...(connection.signalRef === undefined
|
|
439
|
+
? {}
|
|
440
|
+
: { signalRef: parseBuildDataObject(connection.signalRef, `${path}.signalRef`) }),
|
|
441
|
+
...(connection.wire === undefined ? {} : { wire: parseBuildDataObject(connection.wire, `${path}.wire`) }),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function parseBoards(value: YamlValue | undefined): readonly BoardRealization[] | undefined {
|
|
446
|
+
if (value === undefined) {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
return optionalArray(value, 'boards').map(parseBoard);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function parseBoard(value: YamlValue, index: number): BoardRealization {
|
|
453
|
+
const path = `boards[${index}]`;
|
|
454
|
+
const board = expectObject(value, path);
|
|
455
|
+
const sourceCircuit = board.sourceCircuit === undefined
|
|
456
|
+
? undefined
|
|
457
|
+
: parseBoardSourceCircuit(board.sourceCircuit, `${path}.sourceCircuit`);
|
|
458
|
+
return {
|
|
459
|
+
...parseBuildDataObject(board, path),
|
|
460
|
+
id: expectString(board.id, `${path}.id`),
|
|
461
|
+
schema: parseLiteralString(board.schema, `${path}.schema`, 'circuit-board/v1'),
|
|
462
|
+
family: parseBoardFamily(board.family, `${path}.family`),
|
|
463
|
+
kind: parseBoardKind(board.kind, `${path}.kind`),
|
|
464
|
+
...(board.subtype === undefined ? {} : { subtype: parseBoardSubtype(board.subtype, `${path}.subtype`) }),
|
|
465
|
+
...(board.source === undefined ? {} : { source: expectString(board.source, `${path}.source`) }),
|
|
466
|
+
...(board.units === undefined ? {} : { units: expectString(board.units, `${path}.units`) }),
|
|
467
|
+
...(board.locked === undefined ? {} : { locked: expectBoolean(board.locked, `${path}.locked`) }),
|
|
468
|
+
...(sourceCircuit === undefined ? {} : { sourceCircuit }),
|
|
469
|
+
edgeTerminals: optionalArray(board.edgeTerminals, `${path}.edgeTerminals`).map((terminal, terminalIndex) =>
|
|
470
|
+
parseBoardEdgeTerminal(terminal, `${path}.edgeTerminals[${terminalIndex}]`)
|
|
471
|
+
),
|
|
472
|
+
footprintPlacements: optionalArray(board.footprintPlacements, `${path}.footprintPlacements`).map((placement, placementIndex) =>
|
|
473
|
+
parseBoardFootprintPlacement(placement, `${path}.footprintPlacements[${placementIndex}]`)
|
|
474
|
+
),
|
|
475
|
+
...(board.netlist === undefined ? {} : { netlist: parseBoardNetlist(board.netlist, `${path}.netlist`) }),
|
|
476
|
+
routes: optionalArray(board.routes, `${path}.routes`).map((route, routeIndex) =>
|
|
477
|
+
parseBoardRoute(route, `${path}.routes[${routeIndex}]`)
|
|
478
|
+
),
|
|
479
|
+
...(board.zones === undefined ? {} : { zones: parseBuildDataObjectArray(board.zones, `${path}.zones`) }),
|
|
480
|
+
...(board.drills === undefined ? {} : { drills: parseBuildDataObjectArray(board.drills, `${path}.drills`) }),
|
|
481
|
+
...(board.review === undefined ? {} : { review: parseBuildDataObject(board.review, `${path}.review`) }),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function parseBoardSourceCircuit(value: YamlValue, path: string): BoardSourceCircuitHash {
|
|
486
|
+
const sourceCircuit = expectObject(value, path);
|
|
487
|
+
const hash = expectString(sourceCircuit.hash, `${path}.hash`);
|
|
488
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(hash)) {
|
|
489
|
+
throw new Error(`${path}.hash: expected sha256:<64 lowercase hex characters>`);
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
...parseBuildDataObject(sourceCircuit, path),
|
|
493
|
+
schema: parseLiteralString(sourceCircuit.schema, `${path}.schema`, 'canonical-circuit-facts-hash/v1'),
|
|
494
|
+
hashAlgorithm: parseLiteralString(sourceCircuit.hashAlgorithm, `${path}.hashAlgorithm`, 'sha256'),
|
|
495
|
+
hash,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function parseBoardEdgeTerminal(value: YamlValue, path: string): BoardEdgeTerminal {
|
|
500
|
+
const terminal = expectObject(value, path);
|
|
501
|
+
return {
|
|
502
|
+
...parseBuildDataObject(terminal, path),
|
|
503
|
+
id: expectString(terminal.id, `${path}.id`),
|
|
504
|
+
...(terminal.role === undefined ? {} : { role: expectString(terminal.role, `${path}.role`) }),
|
|
505
|
+
...(terminal.terminalRef === undefined
|
|
506
|
+
? {}
|
|
507
|
+
: { terminalRef: parseComponentTerminalRef(terminal.terminalRef, `${path}.terminalRef`) }),
|
|
508
|
+
...(terminal.hole === undefined ? {} : { hole: parseBoardHole(terminal.hole, `${path}.hole`) }),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function parseBoardFootprintPlacement(value: YamlValue, path: string): BoardFootprintPlacement {
|
|
513
|
+
const placement = expectObject(value, path);
|
|
514
|
+
return {
|
|
515
|
+
...parseBuildDataObject(placement, path),
|
|
516
|
+
componentId: expectString(placement.componentId, `${path}.componentId`),
|
|
517
|
+
footprintId: expectString(placement.footprintId, `${path}.footprintId`),
|
|
518
|
+
...(placement.atGrid === undefined ? {} : { atGrid: parseBoardHole(placement.atGrid, `${path}.atGrid`) }),
|
|
519
|
+
...(placement.atMm === undefined ? {} : { atMm: parsePoint(placement.atMm, `${path}.atMm`) }),
|
|
520
|
+
...(placement.rotationDeg === undefined ? {} : { rotationDeg: expectNumber(placement.rotationDeg, `${path}.rotationDeg`) }),
|
|
521
|
+
pads: optionalArray(placement.pads, `${path}.pads`).map((pad, padIndex) =>
|
|
522
|
+
parseBoardPlacedPad(pad, `${path}.pads[${padIndex}]`)
|
|
523
|
+
),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function parseBoardPlacedPad(value: YamlValue, path: string): BoardPlacedPad {
|
|
528
|
+
const pad = expectObject(value, path);
|
|
529
|
+
return {
|
|
530
|
+
...parseBuildDataObject(pad, path),
|
|
531
|
+
padId: expectString(pad.padId, `${path}.padId`),
|
|
532
|
+
...(pad.terminalName === undefined ? {} : { terminalName: expectString(pad.terminalName, `${path}.terminalName`) }),
|
|
533
|
+
...(pad.hole === undefined ? {} : { hole: parseBoardHole(pad.hole, `${path}.hole`) }),
|
|
534
|
+
...(pad.positionMm === undefined ? {} : { positionMm: parsePoint(pad.positionMm, `${path}.positionMm`) }),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function parseBoardNetlist(value: YamlValue, path: string): BoardNetlist {
|
|
539
|
+
const netlist = expectObject(value, path);
|
|
540
|
+
return {
|
|
541
|
+
...parseBuildDataObject(netlist, path),
|
|
542
|
+
...(netlist.source === undefined ? {} : { source: expectString(netlist.source, `${path}.source`) }),
|
|
543
|
+
nets: optionalArray(netlist.nets, `${path}.nets`).map((net, netIndex) =>
|
|
544
|
+
parseBoardNet(net, `${path}.nets[${netIndex}]`)
|
|
545
|
+
),
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function parseBoardNet(value: YamlValue, path: string): BoardNet {
|
|
550
|
+
const net = expectObject(value, path);
|
|
551
|
+
return {
|
|
552
|
+
...parseBuildDataObject(net, path),
|
|
553
|
+
id: expectString(net.id, `${path}.id`),
|
|
554
|
+
...(net.name === undefined ? {} : { name: expectString(net.name, `${path}.name`) }),
|
|
555
|
+
members: optionalArray(net.members, `${path}.members`).map((member, memberIndex) =>
|
|
556
|
+
parseBoardNetMember(member, `${path}.members[${memberIndex}]`)
|
|
557
|
+
),
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function parseBoardNetMember(value: YamlValue, path: string): BoardNetMember {
|
|
562
|
+
const member = expectObject(value, path);
|
|
563
|
+
return {
|
|
564
|
+
...parseBuildDataObject(member, path),
|
|
565
|
+
componentId: expectString(member.componentId, `${path}.componentId`),
|
|
566
|
+
terminalName: expectString(member.terminalName, `${path}.terminalName`),
|
|
567
|
+
...(member.padId === undefined ? {} : { padId: expectString(member.padId, `${path}.padId`) }),
|
|
568
|
+
...(member.terminalId === undefined ? {} : { terminalId: expectString(member.terminalId, `${path}.terminalId`) }),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function parseBoardRoute(value: YamlValue, path: string): BoardRoute {
|
|
573
|
+
const route = expectObject(value, path);
|
|
574
|
+
return {
|
|
575
|
+
...parseBuildDataObject(route, path),
|
|
576
|
+
id: expectString(route.id, `${path}.id`),
|
|
577
|
+
...(route.netRef === undefined ? {} : { netRef: parseBuildDataObject(route.netRef, `${path}.netRef`) }),
|
|
578
|
+
...(route.locked === undefined ? {} : { locked: expectBoolean(route.locked, `${path}.locked`) }),
|
|
579
|
+
...(route.conductors === undefined
|
|
580
|
+
? {}
|
|
581
|
+
: { conductors: parseBuildDataObjectArray(route.conductors, `${path}.conductors`) }),
|
|
582
|
+
...(route.copper === undefined ? {} : { copper: parseBuildDataObjectArray(route.copper, `${path}.copper`) }),
|
|
583
|
+
...(route.vias === undefined ? {} : { vias: parseBuildDataObjectArray(route.vias, `${path}.vias`) }),
|
|
584
|
+
...(route.zones === undefined ? {} : { zones: parseBuildDataObjectArray(route.zones, `${path}.zones`) }),
|
|
585
|
+
...(route.drills === undefined ? {} : { drills: parseBuildDataObjectArray(route.drills, `${path}.drills`) }),
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function parseComponentTerminalRef(value: YamlValue, path: string): ComponentTerminalRef {
|
|
590
|
+
const ref = expectObject(value, path);
|
|
591
|
+
return {
|
|
592
|
+
...parseBuildDataObject(ref, path),
|
|
593
|
+
componentId: expectString(ref.componentId, `${path}.componentId`),
|
|
594
|
+
terminalName: expectString(ref.terminalName, `${path}.terminalName`),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function parseBoardHole(value: YamlValue, path: string) {
|
|
599
|
+
const hole = expectObject(value, path);
|
|
600
|
+
return {
|
|
601
|
+
...parseBuildDataObject(hole, path),
|
|
602
|
+
row: expectPositiveInteger(hole.row, `${path}.row`),
|
|
603
|
+
column: expectPositiveInteger(hole.column, `${path}.column`),
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function parseBoardFamily(value: YamlValue | undefined, path: string): BoardFamily {
|
|
608
|
+
const family = expectString(value, path);
|
|
609
|
+
if (family === 'prototype-board' || family === 'fabricated-board') {
|
|
610
|
+
return family;
|
|
611
|
+
}
|
|
612
|
+
throw new Error(`${path}: expected prototype-board or fabricated-board`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function parseBoardKind(value: YamlValue | undefined, path: string): BoardKind {
|
|
616
|
+
const kind = expectString(value, path);
|
|
617
|
+
switch (kind) {
|
|
618
|
+
case 'stripboard':
|
|
619
|
+
case 'perfboard':
|
|
620
|
+
case 'breadboard-pattern':
|
|
621
|
+
case 'pcb':
|
|
622
|
+
return kind;
|
|
623
|
+
default:
|
|
624
|
+
throw new Error(`${path}: expected stripboard, perfboard, breadboard-pattern, or pcb`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function parseBoardSubtype(value: YamlValue | undefined, path: string): BoardSubtype {
|
|
629
|
+
const subtype = expectString(value, path);
|
|
630
|
+
switch (subtype) {
|
|
631
|
+
case 'veroboard':
|
|
632
|
+
case 'isolated-pad':
|
|
633
|
+
case 'solderable-half-breadboard':
|
|
634
|
+
case 'single-sided-through-hole':
|
|
635
|
+
case 'two-layer-through-hole':
|
|
636
|
+
return subtype;
|
|
637
|
+
default:
|
|
638
|
+
throw new Error(`${path}: expected a supported board subtype`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function parseLiteralString<T extends string>(value: YamlValue | undefined, path: string, expected: T): T {
|
|
643
|
+
const actual = expectString(value, path);
|
|
644
|
+
if (actual === expected) {
|
|
645
|
+
return expected;
|
|
646
|
+
}
|
|
647
|
+
throw new Error(`${path}: expected ${expected}`);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function parseBuildDataObjectArray(value: YamlValue | undefined, path: string): readonly VdspBuildDataObject[] {
|
|
651
|
+
return optionalArray(value, path).map((item, index) => parseBuildDataObject(item, `${path}[${index}]`));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function parseBuildDataObject(value: YamlValue | undefined, path: string): VdspBuildDataObject {
|
|
655
|
+
const object = expectObject(value, path);
|
|
656
|
+
const out: Record<string, VdspBuildDataValue | undefined> = {};
|
|
657
|
+
for (const [key, child] of Object.entries(object)) {
|
|
658
|
+
out[key] = parseBuildDataValue(child, `${path}.${key}`);
|
|
659
|
+
}
|
|
660
|
+
return out;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function parseBuildDataValue(value: YamlValue, path: string): VdspBuildDataValue {
|
|
664
|
+
if (isScalar(value)) {
|
|
665
|
+
return value;
|
|
666
|
+
}
|
|
667
|
+
if (Array.isArray(value)) {
|
|
668
|
+
return value.map((item, index) => parseBuildDataValue(item, `${path}[${index}]`));
|
|
669
|
+
}
|
|
670
|
+
if (isYamlObject(value)) {
|
|
671
|
+
return parseBuildDataObject(value, path);
|
|
672
|
+
}
|
|
673
|
+
throw new Error(`${path}: expected v3 build data value`);
|
|
674
|
+
}
|
|
675
|
+
|
|
94
676
|
function parseControlGroups(value: YamlValue | undefined): readonly ControlGroup[] | undefined {
|
|
95
677
|
if (value === undefined) {
|
|
96
678
|
return undefined;
|
|
@@ -678,7 +1260,7 @@ function parseSource(value: YamlValue | undefined): DocumentSource {
|
|
|
678
1260
|
return parseStringRecord(value, 'source');
|
|
679
1261
|
}
|
|
680
1262
|
|
|
681
|
-
function parsePanel(value: YamlValue | undefined): PanelPlacementMetadata | undefined {
|
|
1263
|
+
function parsePanel(value: YamlValue | undefined, allowV3PhysicalFields: boolean): PanelPlacementMetadata | undefined {
|
|
682
1264
|
if (value === undefined) {
|
|
683
1265
|
return undefined;
|
|
684
1266
|
}
|
|
@@ -686,7 +1268,9 @@ function parsePanel(value: YamlValue | undefined): PanelPlacementMetadata | unde
|
|
|
686
1268
|
|
|
687
1269
|
if (panel.faces !== undefined) {
|
|
688
1270
|
return {
|
|
689
|
-
faces: optionalArray(panel.faces, 'panel.faces').map((item, index) =>
|
|
1271
|
+
faces: optionalArray(panel.faces, 'panel.faces').map((item, index) =>
|
|
1272
|
+
parsePanelFace(item, index, allowV3PhysicalFields)
|
|
1273
|
+
),
|
|
690
1274
|
};
|
|
691
1275
|
}
|
|
692
1276
|
|
|
@@ -701,21 +1285,30 @@ function parsePanel(value: YamlValue | undefined): PanelPlacementMetadata | unde
|
|
|
701
1285
|
faces: [{
|
|
702
1286
|
id: 'top',
|
|
703
1287
|
layout,
|
|
704
|
-
elements: parsePanelElements(elementsValue, layout, elementsPath),
|
|
1288
|
+
elements: parsePanelElements(elementsValue, layout, elementsPath, allowV3PhysicalFields),
|
|
705
1289
|
}],
|
|
706
1290
|
};
|
|
707
1291
|
}
|
|
708
1292
|
|
|
709
|
-
function parsePanelFace(
|
|
1293
|
+
function parsePanelFace(
|
|
1294
|
+
value: YamlValue,
|
|
1295
|
+
index: number,
|
|
1296
|
+
allowV3PhysicalFields: boolean,
|
|
1297
|
+
): PanelPlacementMetadata['faces'][number] {
|
|
710
1298
|
const path = `panel.faces[${index}]`;
|
|
711
1299
|
const face = expectObject(value, path);
|
|
712
1300
|
const label = parseOptionalString(face.label, `${path}.label`);
|
|
713
1301
|
const layout = parsePanelLayout(face.layout, `${path}.layout`);
|
|
1302
|
+
if (!allowV3PhysicalFields && face.geometry !== undefined) {
|
|
1303
|
+
throw new Error(`${path}.geometry: requires schema ${INTERCHANGE_SCHEMA_V3}`);
|
|
1304
|
+
}
|
|
1305
|
+
const geometry = allowV3PhysicalFields ? parseOptionalPanelFaceGeometry(face.geometry, `${path}.geometry`) : undefined;
|
|
714
1306
|
return {
|
|
715
1307
|
id: expectString(face.id, `${path}.id`),
|
|
716
1308
|
...(label === undefined ? {} : { label }),
|
|
717
1309
|
layout,
|
|
718
|
-
|
|
1310
|
+
...(geometry === undefined ? {} : { geometry }),
|
|
1311
|
+
elements: parsePanelElements(face.elements, layout, `${path}.elements`, allowV3PhysicalFields),
|
|
719
1312
|
};
|
|
720
1313
|
}
|
|
721
1314
|
|
|
@@ -737,13 +1330,25 @@ function parsePanelElements(
|
|
|
737
1330
|
value: YamlValue | undefined,
|
|
738
1331
|
layout: PanelGridLayout,
|
|
739
1332
|
path: string,
|
|
1333
|
+
allowV3PhysicalFields: boolean,
|
|
740
1334
|
): PanelPlacementMetadata['faces'][number]['elements'] {
|
|
741
1335
|
return optionalArray(value, path).map((item, index) => {
|
|
742
1336
|
const elementPath = `${path}[${index}]`;
|
|
743
1337
|
const element = expectObject(item, elementPath);
|
|
744
1338
|
const label = parseOptionalString(element.label, `${elementPath}.label`);
|
|
745
1339
|
const interfaceControlId = parseOptionalString(element.interfaceControlId, `${elementPath}.interfaceControlId`);
|
|
1340
|
+
if (!allowV3PhysicalFields && element.id !== undefined) {
|
|
1341
|
+
throw new Error(`${elementPath}.id: requires schema ${INTERCHANGE_SCHEMA_V3}`);
|
|
1342
|
+
}
|
|
1343
|
+
if (!allowV3PhysicalFields && element.physical !== undefined) {
|
|
1344
|
+
throw new Error(`${elementPath}.physical: requires schema ${INTERCHANGE_SCHEMA_V3}`);
|
|
1345
|
+
}
|
|
1346
|
+
const id = allowV3PhysicalFields ? parseOptionalString(element.id, `${elementPath}.id`) : undefined;
|
|
1347
|
+
const physical = allowV3PhysicalFields
|
|
1348
|
+
? parseOptionalPanelElementPhysical(element.physical, `${elementPath}.physical`)
|
|
1349
|
+
: undefined;
|
|
746
1350
|
return {
|
|
1351
|
+
...(id === undefined ? {} : { id }),
|
|
747
1352
|
bind: parsePanelElementBinding(element, elementPath),
|
|
748
1353
|
kind: parsePanelControlKind(
|
|
749
1354
|
element.kind ?? element.controlKind,
|
|
@@ -754,10 +1359,61 @@ function parsePanelElements(
|
|
|
754
1359
|
grid: parsePanelGridPosition(element.grid, `${elementPath}.grid`, layout),
|
|
755
1360
|
...(label === undefined ? {} : { label }),
|
|
756
1361
|
...(interfaceControlId === undefined ? {} : { interfaceControlId }),
|
|
1362
|
+
...(physical === undefined ? {} : { physical }),
|
|
757
1363
|
};
|
|
758
1364
|
});
|
|
759
1365
|
}
|
|
760
1366
|
|
|
1367
|
+
function parseOptionalPanelFaceGeometry(
|
|
1368
|
+
value: YamlValue | undefined,
|
|
1369
|
+
path: string,
|
|
1370
|
+
): PanelFaceGeometry | undefined {
|
|
1371
|
+
if (value === undefined) {
|
|
1372
|
+
return undefined;
|
|
1373
|
+
}
|
|
1374
|
+
const geometry = expectObject(value, path);
|
|
1375
|
+
return {
|
|
1376
|
+
...parseBuildDataObject(geometry, path),
|
|
1377
|
+
...(geometry.units === undefined ? {} : { units: expectString(geometry.units, `${path}.units`) }),
|
|
1378
|
+
...(geometry.surface === undefined ? {} : { surface: expectString(geometry.surface, `${path}.surface`) }),
|
|
1379
|
+
...(geometry.usableRectMm === undefined
|
|
1380
|
+
? {}
|
|
1381
|
+
: { usableRectMm: parseMillimeterRect(geometry.usableRectMm, `${path}.usableRectMm`) }),
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
function parseOptionalPanelElementPhysical(
|
|
1386
|
+
value: YamlValue | undefined,
|
|
1387
|
+
path: string,
|
|
1388
|
+
): PanelElementPhysicalPlacement | undefined {
|
|
1389
|
+
if (value === undefined) {
|
|
1390
|
+
return undefined;
|
|
1391
|
+
}
|
|
1392
|
+
const physical = expectObject(value, path);
|
|
1393
|
+
return {
|
|
1394
|
+
...parseBuildDataObject(physical, path),
|
|
1395
|
+
...(physical.units === undefined ? {} : { units: expectString(physical.units, `${path}.units`) }),
|
|
1396
|
+
...(physical.centerMm === undefined ? {} : { centerMm: parsePoint(physical.centerMm, `${path}.centerMm`) }),
|
|
1397
|
+
...(physical.drillDiameterMm === undefined
|
|
1398
|
+
? {}
|
|
1399
|
+
: { drillDiameterMm: expectNumber(physical.drillDiameterMm, `${path}.drillDiameterMm`) }),
|
|
1400
|
+
...(physical.partProfileId === undefined
|
|
1401
|
+
? {}
|
|
1402
|
+
: { partProfileId: expectString(physical.partProfileId, `${path}.partProfileId`) }),
|
|
1403
|
+
...(physical.locked === undefined ? {} : { locked: expectBoolean(physical.locked, `${path}.locked`) }),
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
function parseMillimeterRect(value: YamlValue | undefined, path: string) {
|
|
1408
|
+
const rect = expectObject(value, path);
|
|
1409
|
+
return {
|
|
1410
|
+
x: expectNumber(rect.x, `${path}.x`),
|
|
1411
|
+
y: expectNumber(rect.y, `${path}.y`),
|
|
1412
|
+
width: expectNumber(rect.width, `${path}.width`),
|
|
1413
|
+
height: expectNumber(rect.height, `${path}.height`),
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
|
|
761
1417
|
function parsePanelElementBinding(element: YamlObject, path: string): PanelElementBinding {
|
|
762
1418
|
if (element.bind !== undefined) {
|
|
763
1419
|
const bind = expectObject(element.bind, `${path}.bind`);
|
|
@@ -857,11 +1513,13 @@ function parsePanelControlKind(value: YamlValue | undefined, path: string): Pane
|
|
|
857
1513
|
case 'knob':
|
|
858
1514
|
case 'slider':
|
|
859
1515
|
case 'switch':
|
|
1516
|
+
case 'selector':
|
|
1517
|
+
case 'footswitch':
|
|
860
1518
|
case 'led':
|
|
861
1519
|
case 'jack':
|
|
862
1520
|
return kind;
|
|
863
1521
|
default:
|
|
864
|
-
throw new Error(`${path}: expected knob, slider, switch, led, or jack`);
|
|
1522
|
+
throw new Error(`${path}: expected knob, slider, switch, selector, footswitch, led, or jack`);
|
|
865
1523
|
}
|
|
866
1524
|
}
|
|
867
1525
|
|