altium-toolkit 1.0.9 → 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/docs/schemas/altium_toolkit/ci_artifact_bundle_a1.schema.json +76 -0
- package/docs/schemas/altium_toolkit/draftsman_digest_a1.schema.json +35 -0
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +6 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +160 -1
- package/docs/schemas/altium_toolkit/parser_compatibility_fuzz_a1.schema.json +25 -0
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +27 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +6 -0
- package/docs/schemas/altium_toolkit/project_document_graph_a1.schema.json +33 -0
- package/docs/schemas/altium_toolkit/svg_model_cross_link_a1.schema.json +39 -0
- package/package.json +1 -1
- package/src/core/altium/AltiumParser.mjs +7 -2
- package/src/core/altium/CiArtifactBundleBuilder.mjs +202 -0
- package/src/core/altium/DraftsmanDigestParser.mjs +689 -0
- package/src/core/altium/ParserCompatibilityFuzzer.mjs +192 -0
- package/src/core/altium/PcbModelParser.mjs +29 -4
- package/src/core/altium/PcbPadStackParser.mjs +171 -2
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +8 -1
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +71 -2
- package/src/core/altium/PcbRouteAnalysisBuilder.mjs +730 -0
- package/src/core/altium/PcbStatisticsBuilder.mjs +9 -0
- package/src/core/altium/PrjPcbModelParser.mjs +24 -2
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +15 -0
- package/src/core/altium/ProjectDocumentGraphBuilder.mjs +280 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +5 -1
- package/src/core/altium/SvgModelCrossLinkValidator.mjs +402 -0
- package/src/core/circuit-json/CircuitJsonModelAdapter.mjs +136 -96
- package/src/core/circuit-json/CircuitJsonModelAdapterPcbElements.mjs +244 -0
- package/src/core/circuit-json/CircuitJsonModelSchema.mjs +1 -1
- package/src/parser.mjs +6 -0
- package/src/ui/PcbSvgRenderer.mjs +65 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
import { PcbSvgRenderer } from '../../ui/PcbSvgRenderer.mjs'
|
|
6
|
+
import { SchematicSvgRenderer } from '../../ui/SchematicSvgRenderer.mjs'
|
|
7
|
+
import { PcbStatisticsBuilder } from './PcbStatisticsBuilder.mjs'
|
|
8
|
+
import { ProjectDesignBundleBuilder } from './ProjectDesignBundleBuilder.mjs'
|
|
9
|
+
import { ProjectDocumentGraphBuilder } from './ProjectDocumentGraphBuilder.mjs'
|
|
10
|
+
import { ProjectNetlistExporter } from './ProjectNetlistExporter.mjs'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Builds one deterministic CI artifact package from parsed project documents.
|
|
14
|
+
*/
|
|
15
|
+
export class CiArtifactBundleBuilder {
|
|
16
|
+
static SCHEMA = 'altium-toolkit.ci.artifact-bundle.a1'
|
|
17
|
+
|
|
18
|
+
static #UNITS = {
|
|
19
|
+
coordinate: 'mil',
|
|
20
|
+
length: 'mil',
|
|
21
|
+
board: 'mil',
|
|
22
|
+
pnp: 'mil',
|
|
23
|
+
angle: 'deg'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static #PNP_UNITS = {
|
|
27
|
+
coordinate: 'mil',
|
|
28
|
+
angle: 'deg'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Builds a deterministic bundle of normalized, rendered, and report outputs.
|
|
33
|
+
* @param {{ projectModel?: object, documentModels?: object[], designBundle?: object, annotationModels?: object[], variantName?: string, renderSchematicSvg?: boolean, renderPcbLayerSvgs?: boolean, schematicSvgOptions?: object }} options Bundle options.
|
|
34
|
+
* @returns {object}
|
|
35
|
+
*/
|
|
36
|
+
static build(options = {}) {
|
|
37
|
+
const documentModels = Array.isArray(options.documentModels)
|
|
38
|
+
? options.documentModels
|
|
39
|
+
: []
|
|
40
|
+
const designBundle =
|
|
41
|
+
options.designBundle ||
|
|
42
|
+
ProjectDesignBundleBuilder.build({
|
|
43
|
+
projectModel: options.projectModel,
|
|
44
|
+
documentModels,
|
|
45
|
+
annotationModels: options.annotationModels || [],
|
|
46
|
+
variantName: options.variantName
|
|
47
|
+
})
|
|
48
|
+
const activeBundle = designBundle.effectiveVariant || designBundle
|
|
49
|
+
const schematicSvgs =
|
|
50
|
+
options.renderSchematicSvg === false
|
|
51
|
+
? []
|
|
52
|
+
: CiArtifactBundleBuilder.#schematicSvgs(
|
|
53
|
+
documentModels,
|
|
54
|
+
options.schematicSvgOptions || {}
|
|
55
|
+
)
|
|
56
|
+
const pcbLayerSvgs =
|
|
57
|
+
options.renderPcbLayerSvgs === false
|
|
58
|
+
? []
|
|
59
|
+
: CiArtifactBundleBuilder.#pcbLayerSvgs(documentModels)
|
|
60
|
+
const statistics = CiArtifactBundleBuilder.#statistics(documentModels)
|
|
61
|
+
const diagnostics = CiArtifactBundleBuilder.#diagnostics(
|
|
62
|
+
designBundle,
|
|
63
|
+
documentModels
|
|
64
|
+
)
|
|
65
|
+
const netlistJson =
|
|
66
|
+
ProjectNetlistExporter.buildNetlistJson(activeBundle)
|
|
67
|
+
const documentGraph =
|
|
68
|
+
designBundle.project?.documentGraph ||
|
|
69
|
+
ProjectDocumentGraphBuilder.build(
|
|
70
|
+
options.projectModel?.project || designBundle.project || {}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
schema: CiArtifactBundleBuilder.SCHEMA,
|
|
75
|
+
summary: {
|
|
76
|
+
normalizedModelCount: documentModels.length,
|
|
77
|
+
schematicSvgCount: schematicSvgs.length,
|
|
78
|
+
pcbLayerSvgCount: pcbLayerSvgs.reduce(
|
|
79
|
+
(total, entry) => total + entry.layers.length,
|
|
80
|
+
0
|
|
81
|
+
),
|
|
82
|
+
netCount: netlistJson.nets.length,
|
|
83
|
+
bomRowCount: (activeBundle.bom || designBundle.bom || [])
|
|
84
|
+
.length,
|
|
85
|
+
pnpCount: (activeBundle.pnp?.entries || []).length,
|
|
86
|
+
diagnosticCount: diagnostics.length
|
|
87
|
+
},
|
|
88
|
+
units: designBundle.units || CiArtifactBundleBuilder.#UNITS,
|
|
89
|
+
designBundle,
|
|
90
|
+
documentGraph,
|
|
91
|
+
normalizedModels: documentModels,
|
|
92
|
+
netlist: {
|
|
93
|
+
json: netlistJson,
|
|
94
|
+
wirelist: ProjectNetlistExporter.buildWirelist(activeBundle)
|
|
95
|
+
},
|
|
96
|
+
bom: {
|
|
97
|
+
rows: activeBundle.bom || designBundle.bom || []
|
|
98
|
+
},
|
|
99
|
+
pnp: CiArtifactBundleBuilder.#pnp(activeBundle, designBundle),
|
|
100
|
+
schematicSvgs,
|
|
101
|
+
pcbLayerSvgs,
|
|
102
|
+
statistics,
|
|
103
|
+
diagnostics
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolves a PnP payload with explicit output units.
|
|
109
|
+
* @param {object} activeBundle Effective bundle or variant.
|
|
110
|
+
* @param {object} designBundle Source design bundle.
|
|
111
|
+
* @returns {object}
|
|
112
|
+
*/
|
|
113
|
+
static #pnp(activeBundle, designBundle) {
|
|
114
|
+
const pnp = activeBundle.pnp || designBundle.pnp || { entries: [] }
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
units: pnp.units || CiArtifactBundleBuilder.#PNP_UNITS,
|
|
118
|
+
...pnp
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Renders schematic SVG entries.
|
|
124
|
+
* @param {object[]} documentModels Parsed document models.
|
|
125
|
+
* @param {object} renderOptions Schematic SVG render options.
|
|
126
|
+
* @returns {object[]}
|
|
127
|
+
*/
|
|
128
|
+
static #schematicSvgs(documentModels, renderOptions) {
|
|
129
|
+
return documentModels
|
|
130
|
+
.filter((model) => model?.kind === 'schematic')
|
|
131
|
+
.map((model) => ({
|
|
132
|
+
fileName: model.fileName || '',
|
|
133
|
+
svg: SchematicSvgRenderer.render(model, renderOptions)
|
|
134
|
+
}))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Renders per-layer PCB SVG entries.
|
|
139
|
+
* @param {object[]} documentModels Parsed document models.
|
|
140
|
+
* @returns {object[]}
|
|
141
|
+
*/
|
|
142
|
+
static #pcbLayerSvgs(documentModels) {
|
|
143
|
+
return documentModels
|
|
144
|
+
.filter((model) => model?.kind === 'pcb')
|
|
145
|
+
.map((model) => ({
|
|
146
|
+
fileName: model.fileName || '',
|
|
147
|
+
layers: PcbSvgRenderer.renderLayerSvgs(model)
|
|
148
|
+
}))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Builds statistics package entries.
|
|
153
|
+
* @param {object[]} documentModels Parsed document models.
|
|
154
|
+
* @returns {{ pcb: object[] }}
|
|
155
|
+
*/
|
|
156
|
+
static #statistics(documentModels) {
|
|
157
|
+
return {
|
|
158
|
+
pcb: documentModels
|
|
159
|
+
.filter((model) => model?.kind === 'pcb')
|
|
160
|
+
.map((model) => ({
|
|
161
|
+
fileName: model.fileName || '',
|
|
162
|
+
statistics:
|
|
163
|
+
model.pcb?.statistics ||
|
|
164
|
+
PcbStatisticsBuilder.build(model.pcb || {})
|
|
165
|
+
}))
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Collects diagnostics from the bundle and source documents.
|
|
171
|
+
* @param {object} designBundle Composed design bundle.
|
|
172
|
+
* @param {object[]} documentModels Parsed document models.
|
|
173
|
+
* @returns {object[]}
|
|
174
|
+
*/
|
|
175
|
+
static #diagnostics(designBundle, documentModels) {
|
|
176
|
+
return [
|
|
177
|
+
...CiArtifactBundleBuilder.#sourceDiagnostics(
|
|
178
|
+
'design-bundle',
|
|
179
|
+
designBundle.diagnostics || []
|
|
180
|
+
),
|
|
181
|
+
...documentModels.flatMap((model) =>
|
|
182
|
+
CiArtifactBundleBuilder.#sourceDiagnostics(
|
|
183
|
+
model.fileName || model.kind || 'document',
|
|
184
|
+
model.diagnostics || []
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Adds source labels to diagnostics without changing their codes.
|
|
192
|
+
* @param {string} source Diagnostic source label.
|
|
193
|
+
* @param {object[]} diagnostics Source diagnostics.
|
|
194
|
+
* @returns {object[]}
|
|
195
|
+
*/
|
|
196
|
+
static #sourceDiagnostics(source, diagnostics) {
|
|
197
|
+
return (diagnostics || []).map((diagnostic) => ({
|
|
198
|
+
source,
|
|
199
|
+
...diagnostic
|
|
200
|
+
}))
|
|
201
|
+
}
|
|
202
|
+
}
|