circuitjson-toolkit 1.0.2 → 1.0.10
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 +21 -2
- package/docs/api.md +50 -4
- package/docs/model-format.md +6 -3
- package/package.json +1 -1
- package/spec/library-scope.md +4 -1
- package/src/core/CircuitJsonBomBuilder.mjs +146 -0
- package/src/core/CircuitJsonDocument.mjs +46 -13
- package/src/core/CircuitJsonElementValidator.mjs +773 -0
- package/src/core/CircuitJsonIndexer.mjs +268 -4
- package/src/core/CircuitJsonManufacturingBuilder.mjs +426 -0
- package/src/core/CircuitJsonParser.mjs +22 -6
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +259 -0
- package/src/core/CircuitJsonUnits.mjs +133 -8
- package/src/core/spice/SpiceCompatibilityPreprocessor.mjs +139 -0
- package/src/core/spice/SpiceDirectiveParser.mjs +231 -0
- package/src/core/spice/SpiceFallbackSimulationEngine.mjs +168 -0
- package/src/core/spice/SpiceSimulationDiagnostics.mjs +234 -0
- package/src/core/spice/SpiceSimulationGraphBuilder.mjs +421 -0
- package/src/core/spice/SpiceSimulationGraphSummary.mjs +90 -0
- package/src/core/spice/SpiceSimulationService.mjs +92 -0
- package/src/core/spice/SpiceTimeSeriesNormalizer.mjs +132 -0
- package/src/index.mjs +6 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { CircuitJsonIndexer } from './CircuitJsonIndexer.mjs'
|
|
2
|
+
import { CircuitJsonUnits } from './CircuitJsonUnits.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds manufacturing-oriented metadata from element arrays.
|
|
6
|
+
*/
|
|
7
|
+
export class CircuitJsonManufacturingBuilder {
|
|
8
|
+
/**
|
|
9
|
+
* Builds pick-and-place rows and routing exchange text.
|
|
10
|
+
* @param {object[]} circuitJson Parsed element array.
|
|
11
|
+
* @param {{ elementsByType?: Map<string, object[]>, sourceComponentById?: Map<string, object> }} [index] Optional index.
|
|
12
|
+
* @returns {{ pickAndPlaceRows: object[], routingDsn: string }}
|
|
13
|
+
*/
|
|
14
|
+
static build(circuitJson, index = CircuitJsonIndexer.index(circuitJson)) {
|
|
15
|
+
return {
|
|
16
|
+
pickAndPlaceRows:
|
|
17
|
+
CircuitJsonManufacturingBuilder.#pickAndPlaceRows(index),
|
|
18
|
+
routingDsn: CircuitJsonManufacturingBuilder.#routingDsn(index)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds assembly placement rows.
|
|
24
|
+
* @param {{ elementsByType?: Map<string, object[]>, sourceComponentById?: Map<string, object> }} index Element index.
|
|
25
|
+
* @returns {object[]}
|
|
26
|
+
*/
|
|
27
|
+
static #pickAndPlaceRows(index) {
|
|
28
|
+
return CircuitJsonManufacturingBuilder.#all(index, 'pcb_component').map(
|
|
29
|
+
(component) =>
|
|
30
|
+
CircuitJsonManufacturingBuilder.#pickAndPlaceRow(
|
|
31
|
+
component,
|
|
32
|
+
index.sourceComponentById || new Map()
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Builds one placement row.
|
|
39
|
+
* @param {object} component PCB component element.
|
|
40
|
+
* @param {Map<string, object>} sourceComponentById Source lookup.
|
|
41
|
+
* @returns {object}
|
|
42
|
+
*/
|
|
43
|
+
static #pickAndPlaceRow(component, sourceComponentById) {
|
|
44
|
+
const sourceId = String(component.source_component_id || '').trim()
|
|
45
|
+
const source = sourceComponentById.get(sourceId) || {}
|
|
46
|
+
const center = CircuitJsonUnits.optionalPoint(
|
|
47
|
+
component.center || component
|
|
48
|
+
) || {
|
|
49
|
+
x: 0,
|
|
50
|
+
y: 0
|
|
51
|
+
}
|
|
52
|
+
const layer = CircuitJsonManufacturingBuilder.#layer(component.layer)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
designator: CircuitJsonManufacturingBuilder.#designator(
|
|
56
|
+
component,
|
|
57
|
+
source
|
|
58
|
+
),
|
|
59
|
+
componentId: String(component.pcb_component_id || ''),
|
|
60
|
+
sourceComponentId: sourceId,
|
|
61
|
+
x: CircuitJsonManufacturingBuilder.#round(center.x),
|
|
62
|
+
y: CircuitJsonManufacturingBuilder.#round(center.y),
|
|
63
|
+
rotation: CircuitJsonUnits.angle(
|
|
64
|
+
component.rotation ?? component.ccw_rotation,
|
|
65
|
+
0
|
|
66
|
+
),
|
|
67
|
+
layer,
|
|
68
|
+
side: CircuitJsonManufacturingBuilder.#side(layer),
|
|
69
|
+
value: CircuitJsonManufacturingBuilder.#value(source),
|
|
70
|
+
package: String(
|
|
71
|
+
source.ftype || source.package || source.footprint || ''
|
|
72
|
+
),
|
|
73
|
+
manufacturerPartNumber: String(
|
|
74
|
+
source.manufacturer_part_number ||
|
|
75
|
+
source.manufacturerPartNumber ||
|
|
76
|
+
''
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Builds a compact routing exchange text payload.
|
|
83
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
static #routingDsn(index) {
|
|
87
|
+
const lines = ['(pcb assembly)', ' (unit mm)']
|
|
88
|
+
lines.push(...CircuitJsonManufacturingBuilder.#boardLines(index))
|
|
89
|
+
lines.push(...CircuitJsonManufacturingBuilder.#placementLines(index))
|
|
90
|
+
lines.push(...CircuitJsonManufacturingBuilder.#networkLines(index))
|
|
91
|
+
lines.push(')')
|
|
92
|
+
return lines.join('\n')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Builds board structure lines.
|
|
97
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
98
|
+
* @returns {string[]}
|
|
99
|
+
*/
|
|
100
|
+
static #boardLines(index) {
|
|
101
|
+
const board = CircuitJsonManufacturingBuilder.#all(
|
|
102
|
+
index,
|
|
103
|
+
'pcb_board'
|
|
104
|
+
)[0]
|
|
105
|
+
const center = CircuitJsonUnits.optionalPoint(
|
|
106
|
+
board?.center || board
|
|
107
|
+
) || {
|
|
108
|
+
x: 0,
|
|
109
|
+
y: 0
|
|
110
|
+
}
|
|
111
|
+
const width = CircuitJsonUnits.length(board?.width, 0)
|
|
112
|
+
const height = CircuitJsonUnits.length(board?.height, 0)
|
|
113
|
+
const minX = CircuitJsonManufacturingBuilder.#round(
|
|
114
|
+
center.x - width / 2
|
|
115
|
+
)
|
|
116
|
+
const minY = CircuitJsonManufacturingBuilder.#round(
|
|
117
|
+
center.y - height / 2
|
|
118
|
+
)
|
|
119
|
+
const maxX = CircuitJsonManufacturingBuilder.#round(
|
|
120
|
+
center.x + width / 2
|
|
121
|
+
)
|
|
122
|
+
const maxY = CircuitJsonManufacturingBuilder.#round(
|
|
123
|
+
center.y + height / 2
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return [
|
|
127
|
+
' (structure',
|
|
128
|
+
' (boundary (rect ' + [minX, minY, maxX, maxY].join(' ') + '))',
|
|
129
|
+
...CircuitJsonManufacturingBuilder.#layers(board).map(
|
|
130
|
+
(layer) => ' (layer ' + layer + ' signal)'
|
|
131
|
+
),
|
|
132
|
+
' )'
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Builds component placement lines.
|
|
138
|
+
* @param {{ elementsByType?: Map<string, object[]>, sourceComponentById?: Map<string, object> }} index Element index.
|
|
139
|
+
* @returns {string[]}
|
|
140
|
+
*/
|
|
141
|
+
static #placementLines(index) {
|
|
142
|
+
return [
|
|
143
|
+
' (placement',
|
|
144
|
+
...CircuitJsonManufacturingBuilder.#pickAndPlaceRows(index).map(
|
|
145
|
+
(row) =>
|
|
146
|
+
' (component ' +
|
|
147
|
+
CircuitJsonManufacturingBuilder.#token(row.designator) +
|
|
148
|
+
' (place ' +
|
|
149
|
+
[
|
|
150
|
+
row.x,
|
|
151
|
+
row.y,
|
|
152
|
+
row.side || row.layer || 'top',
|
|
153
|
+
row.rotation
|
|
154
|
+
].join(' ') +
|
|
155
|
+
'))'
|
|
156
|
+
),
|
|
157
|
+
' )'
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Builds network lines for nets, pads, vias, and wires.
|
|
163
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
164
|
+
* @returns {string[]}
|
|
165
|
+
*/
|
|
166
|
+
static #networkLines(index) {
|
|
167
|
+
return [
|
|
168
|
+
' (network',
|
|
169
|
+
...CircuitJsonManufacturingBuilder.#netNames(index).flatMap(
|
|
170
|
+
(netName) => [
|
|
171
|
+
' (net ' +
|
|
172
|
+
CircuitJsonManufacturingBuilder.#token(netName),
|
|
173
|
+
...CircuitJsonManufacturingBuilder.#pinLines(
|
|
174
|
+
index,
|
|
175
|
+
netName
|
|
176
|
+
),
|
|
177
|
+
...CircuitJsonManufacturingBuilder.#wireLines(
|
|
178
|
+
index,
|
|
179
|
+
netName
|
|
180
|
+
),
|
|
181
|
+
' )'
|
|
182
|
+
]
|
|
183
|
+
),
|
|
184
|
+
' )'
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Builds pad pin lines for one net.
|
|
190
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
191
|
+
* @param {string} netName Net name.
|
|
192
|
+
* @returns {string[]}
|
|
193
|
+
*/
|
|
194
|
+
static #pinLines(index, netName) {
|
|
195
|
+
return CircuitJsonManufacturingBuilder.#all(index, 'pcb_smtpad')
|
|
196
|
+
.filter(
|
|
197
|
+
(pad) =>
|
|
198
|
+
CircuitJsonManufacturingBuilder.#netName(pad) === netName
|
|
199
|
+
)
|
|
200
|
+
.map((pad) => {
|
|
201
|
+
const point = CircuitJsonUnits.optionalPoint(
|
|
202
|
+
pad.center || pad
|
|
203
|
+
) || {
|
|
204
|
+
x: 0,
|
|
205
|
+
y: 0
|
|
206
|
+
}
|
|
207
|
+
return (
|
|
208
|
+
' (pin ' +
|
|
209
|
+
CircuitJsonManufacturingBuilder.#token(
|
|
210
|
+
pad.pcb_smtpad_id || ''
|
|
211
|
+
) +
|
|
212
|
+
' ' +
|
|
213
|
+
[
|
|
214
|
+
CircuitJsonManufacturingBuilder.#round(point.x),
|
|
215
|
+
CircuitJsonManufacturingBuilder.#round(point.y)
|
|
216
|
+
].join(' ') +
|
|
217
|
+
')'
|
|
218
|
+
)
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Builds routed wire lines for one net.
|
|
224
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
225
|
+
* @param {string} netName Net name.
|
|
226
|
+
* @returns {string[]}
|
|
227
|
+
*/
|
|
228
|
+
static #wireLines(index, netName) {
|
|
229
|
+
return CircuitJsonManufacturingBuilder.#all(index, 'pcb_trace')
|
|
230
|
+
.filter(
|
|
231
|
+
(trace) =>
|
|
232
|
+
CircuitJsonManufacturingBuilder.#netName(trace) === netName
|
|
233
|
+
)
|
|
234
|
+
.flatMap((trace) =>
|
|
235
|
+
CircuitJsonManufacturingBuilder.#traceWireLines(trace)
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Builds routed wire lines for one trace.
|
|
241
|
+
* @param {object} trace Trace element.
|
|
242
|
+
* @returns {string[]}
|
|
243
|
+
*/
|
|
244
|
+
static #traceWireLines(trace) {
|
|
245
|
+
const route = Array.isArray(trace.route) ? trace.route : []
|
|
246
|
+
const lines = []
|
|
247
|
+
let previous = null
|
|
248
|
+
for (const entry of route) {
|
|
249
|
+
const current = CircuitJsonUnits.optionalPoint(
|
|
250
|
+
entry.center || entry
|
|
251
|
+
)
|
|
252
|
+
if (!current) continue
|
|
253
|
+
if (previous) {
|
|
254
|
+
const layer = CircuitJsonManufacturingBuilder.#layer(
|
|
255
|
+
entry.layer || previous.layer || trace.layer
|
|
256
|
+
)
|
|
257
|
+
const width = CircuitJsonUnits.length(
|
|
258
|
+
entry.width ?? previous.width ?? trace.width,
|
|
259
|
+
0
|
|
260
|
+
)
|
|
261
|
+
lines.push(
|
|
262
|
+
' (wire ' +
|
|
263
|
+
[
|
|
264
|
+
layer || 'top',
|
|
265
|
+
CircuitJsonManufacturingBuilder.#round(previous.x),
|
|
266
|
+
CircuitJsonManufacturingBuilder.#round(previous.y),
|
|
267
|
+
CircuitJsonManufacturingBuilder.#round(current.x),
|
|
268
|
+
CircuitJsonManufacturingBuilder.#round(current.y),
|
|
269
|
+
CircuitJsonManufacturingBuilder.#round(width)
|
|
270
|
+
].join(' ') +
|
|
271
|
+
')'
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
previous = { ...entry, x: current.x, y: current.y }
|
|
275
|
+
}
|
|
276
|
+
return lines
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Builds sorted net names.
|
|
281
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
282
|
+
* @returns {string[]}
|
|
283
|
+
*/
|
|
284
|
+
static #netNames(index) {
|
|
285
|
+
const names = new Set()
|
|
286
|
+
for (const net of CircuitJsonManufacturingBuilder.#all(
|
|
287
|
+
index,
|
|
288
|
+
'source_net'
|
|
289
|
+
)) {
|
|
290
|
+
const name = String(net.name || net.source_net_id || '').trim()
|
|
291
|
+
if (name) names.add(name)
|
|
292
|
+
}
|
|
293
|
+
for (const type of ['pcb_smtpad', 'pcb_trace', 'pcb_via']) {
|
|
294
|
+
for (const element of CircuitJsonManufacturingBuilder.#all(
|
|
295
|
+
index,
|
|
296
|
+
type
|
|
297
|
+
)) {
|
|
298
|
+
const name = CircuitJsonManufacturingBuilder.#netName(element)
|
|
299
|
+
if (name) names.add(name)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return [...names].sort((left, right) => left.localeCompare(right))
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Resolves layer names from board metadata.
|
|
307
|
+
* @param {object | undefined} board Board element.
|
|
308
|
+
* @returns {string[]}
|
|
309
|
+
*/
|
|
310
|
+
static #layers(board) {
|
|
311
|
+
const count = Math.max(1, Math.round(Number(board?.num_layers || 2)))
|
|
312
|
+
if (count === 1) return ['top']
|
|
313
|
+
return [
|
|
314
|
+
'top',
|
|
315
|
+
...Array.from(
|
|
316
|
+
{ length: Math.max(count - 2, 0) },
|
|
317
|
+
(_entry, index) => 'inner' + (index + 1)
|
|
318
|
+
),
|
|
319
|
+
'bottom'
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Resolves rows by type.
|
|
325
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Element index.
|
|
326
|
+
* @param {string} type Element type.
|
|
327
|
+
* @returns {object[]}
|
|
328
|
+
*/
|
|
329
|
+
static #all(index, type) {
|
|
330
|
+
return index.elementsByType?.get(type) || []
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Resolves a component designator.
|
|
335
|
+
* @param {object} component PCB component.
|
|
336
|
+
* @param {object} source Source component.
|
|
337
|
+
* @returns {string}
|
|
338
|
+
*/
|
|
339
|
+
static #designator(component, source) {
|
|
340
|
+
return String(
|
|
341
|
+
source.name ||
|
|
342
|
+
source.reference ||
|
|
343
|
+
source.designator ||
|
|
344
|
+
component.name ||
|
|
345
|
+
component.pcb_component_id ||
|
|
346
|
+
''
|
|
347
|
+
).trim()
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Resolves a source value field.
|
|
352
|
+
* @param {object} source Source component.
|
|
353
|
+
* @returns {string}
|
|
354
|
+
*/
|
|
355
|
+
static #value(source) {
|
|
356
|
+
return String(
|
|
357
|
+
source.value ||
|
|
358
|
+
source.resistance ||
|
|
359
|
+
source.capacitance ||
|
|
360
|
+
source.inductance ||
|
|
361
|
+
''
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Resolves a normalized net name.
|
|
367
|
+
* @param {object} element Element row.
|
|
368
|
+
* @returns {string}
|
|
369
|
+
*/
|
|
370
|
+
static #netName(element) {
|
|
371
|
+
return String(
|
|
372
|
+
element?.netName ??
|
|
373
|
+
element?.net ??
|
|
374
|
+
element?.net_name ??
|
|
375
|
+
element?.source_net_name ??
|
|
376
|
+
''
|
|
377
|
+
).trim()
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Resolves a layer string.
|
|
382
|
+
* @param {unknown} value Layer candidate.
|
|
383
|
+
* @returns {string}
|
|
384
|
+
*/
|
|
385
|
+
static #layer(value) {
|
|
386
|
+
const raw =
|
|
387
|
+
typeof value === 'object' && value !== null ? value.name : value
|
|
388
|
+
const text = String(raw ?? '').trim()
|
|
389
|
+
const lowered = text.toLowerCase()
|
|
390
|
+
if (['front', 'f.cu', '1'].includes(lowered)) return 'top'
|
|
391
|
+
if (['back', 'b.cu', '32'].includes(lowered)) return 'bottom'
|
|
392
|
+
return text || 'top'
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Resolves an assembly side.
|
|
397
|
+
* @param {string} layer Layer name.
|
|
398
|
+
* @returns {'top' | 'bottom' | ''}
|
|
399
|
+
*/
|
|
400
|
+
static #side(layer) {
|
|
401
|
+
const text = String(layer || '').toLowerCase()
|
|
402
|
+
if (/\b(bottom|back)\b|\bb[._-]/u.test(text)) return 'bottom'
|
|
403
|
+
if (/\b(top|front)\b|\bf[._-]/u.test(text)) return 'top'
|
|
404
|
+
return ''
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Builds a DSN-safe token.
|
|
409
|
+
* @param {unknown} value Raw value.
|
|
410
|
+
* @returns {string}
|
|
411
|
+
*/
|
|
412
|
+
static #token(value) {
|
|
413
|
+
return String(value || 'unnamed').replace(/[^A-Za-z0-9_.:-]+/gu, '_')
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Rounds a numeric value for deterministic output.
|
|
418
|
+
* @param {number} value Number.
|
|
419
|
+
* @returns {number}
|
|
420
|
+
*/
|
|
421
|
+
static #round(value) {
|
|
422
|
+
const number = Number(value)
|
|
423
|
+
if (!Number.isFinite(number)) return 0
|
|
424
|
+
return Number(number.toFixed(6))
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { CircuitJsonDocument } from './CircuitJsonDocument.mjs'
|
|
2
|
+
import { CircuitJsonBomBuilder } from './CircuitJsonBomBuilder.mjs'
|
|
3
|
+
import { CircuitJsonIndexer } from './CircuitJsonIndexer.mjs'
|
|
4
|
+
import { CircuitJsonManufacturingBuilder } from './CircuitJsonManufacturingBuilder.mjs'
|
|
5
|
+
import { CircuitJsonSupportMatrixBuilder } from './CircuitJsonSupportMatrixBuilder.mjs'
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Parses standalone CircuitJSON files.
|
|
@@ -22,10 +26,15 @@ export class CircuitJsonParser {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
CircuitJsonDocument.assertModel(parsed)
|
|
29
|
+
const index = CircuitJsonIndexer.index(parsed)
|
|
25
30
|
return CircuitJsonDocument.attachMetadata(parsed, {
|
|
26
31
|
fileName: options.fileName || '',
|
|
27
32
|
fileType: 'circuitjson',
|
|
28
|
-
kind: CircuitJsonParser.#resolveKind(
|
|
33
|
+
kind: CircuitJsonParser.#resolveKind(index),
|
|
34
|
+
diagnostics: index.diagnostics,
|
|
35
|
+
bom: CircuitJsonBomBuilder.build(parsed),
|
|
36
|
+
supportMatrix: CircuitJsonSupportMatrixBuilder.build(parsed),
|
|
37
|
+
manufacturing: CircuitJsonManufacturingBuilder.build(parsed, index)
|
|
29
38
|
})
|
|
30
39
|
}
|
|
31
40
|
|
|
@@ -44,12 +53,19 @@ export class CircuitJsonParser {
|
|
|
44
53
|
|
|
45
54
|
/**
|
|
46
55
|
* Resolves a broad document kind from available elements.
|
|
47
|
-
* @param {object[]}
|
|
56
|
+
* @param {{ elementsByType?: Map<string, object[]> }} index Model index.
|
|
48
57
|
* @returns {string}
|
|
49
58
|
*/
|
|
50
|
-
static #resolveKind(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
static #resolveKind(index) {
|
|
60
|
+
if (index.elementsByType?.has('pcb_board')) return 'pcb'
|
|
61
|
+
if (
|
|
62
|
+
[...(index.elementsByType?.keys() || [])].some((type) =>
|
|
63
|
+
String(type).startsWith('schematic_')
|
|
64
|
+
)
|
|
65
|
+
) {
|
|
66
|
+
return 'schematic'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 'circuitjson'
|
|
54
70
|
}
|
|
55
71
|
}
|