altium-toolkit 1.1.22 → 1.1.24
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 +34 -5
- package/docs/api.md +134 -23
- package/docs/model-format.md +174 -20
- package/docs/schemas/altium_toolkit/embedded_assets_a1.schema.json +56 -0
- package/docs/schemas/altium_toolkit/fixture_coverage_matrix_a1.schema.json +89 -0
- package/docs/schemas/altium_toolkit/geometry_bounds_a1.schema.json +86 -0
- package/docs/schemas/altium_toolkit/library_catalog_a1.schema.json +65 -0
- package/docs/schemas/altium_toolkit/library_diff_a1.schema.json +54 -0
- package/docs/schemas/altium_toolkit/library_inspection_a1.schema.json +94 -0
- package/docs/schemas/altium_toolkit/library_qa_a1.schema.json +4 -0
- package/docs/schemas/altium_toolkit/native_stream_inventory_a1.schema.json +66 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +511 -1
- package/docs/schemas/altium_toolkit/parameter_record_inventory_a1.schema.json +84 -0
- package/docs/schemas/altium_toolkit/parser_diagnostics_a1.schema.json +63 -0
- package/docs/schemas/altium_toolkit/parser_value_verification_a1.schema.json +74 -0
- package/docs/schemas/altium_toolkit/pcb_class_report_a1.schema.json +79 -0
- package/docs/schemas/altium_toolkit/pcb_inspection_a1.schema.json +65 -0
- package/docs/schemas/altium_toolkit/pcb_net_membership_a1.schema.json +98 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +3 -0
- package/docs/schemas/altium_toolkit/project_hierarchy_a1.schema.json +79 -0
- package/docs/schemas/altium_toolkit/unsupported_features_a1.schema.json +212 -0
- package/docs/testing.md +2 -0
- package/examples/README.md +21 -0
- package/examples/cli-utils.mjs +148 -0
- package/examples/corpus-smoke.mjs +523 -0
- package/examples/extract-bom.mjs +47 -0
- package/examples/generate-pnp.mjs +59 -0
- package/examples/inspect-board.mjs +70 -0
- package/examples/inspect-schematic.mjs +406 -0
- package/examples/library-catalog.mjs +115 -0
- package/examples/net-report.mjs +61 -0
- package/examples/validate-library.mjs +59 -0
- package/package.json +1 -1
- package/src/core/BinaryReader.mjs +213 -2
- package/src/core/altium/AltiumParser.mjs +352 -14
- package/src/core/altium/AltiumUnits.mjs +205 -0
- package/src/core/altium/AsciiRecordParser.mjs +9 -0
- package/src/core/altium/EmbeddedAssetReportBuilder.mjs +383 -0
- package/src/core/altium/FixtureCoverageMatrixBuilder.mjs +304 -0
- package/src/core/altium/GeometryBoundsReportBuilder.mjs +935 -0
- package/src/core/altium/LibraryCatalogArtifactBuilder.mjs +296 -0
- package/src/core/altium/LibraryDiffReportBuilder.mjs +260 -0
- package/src/core/altium/LibraryInspectionReportBuilder.mjs +156 -0
- package/src/core/altium/LibraryQaReportBuilder.mjs +374 -1
- package/src/core/altium/NativeStreamInventoryBuilder.mjs +177 -0
- package/src/core/altium/NormalizedModelSchema.mjs +3 -31
- package/src/core/altium/ParameterCollection.mjs +431 -0
- package/src/core/altium/ParameterRecordInventoryBuilder.mjs +274 -0
- package/src/core/altium/ParserCompatibilityFuzzer.mjs +106 -2
- package/src/core/altium/ParserDiagnosticNormalizer.mjs +213 -0
- package/src/core/altium/ParserErrors.mjs +90 -0
- package/src/core/altium/ParserFieldCoverageReportBuilder.mjs +656 -0
- package/src/core/altium/ParserUtils.mjs +24 -0
- package/src/core/altium/ParserValueVerificationReportBuilder.mjs +323 -0
- package/src/core/altium/PcbClassReportBuilder.mjs +366 -0
- package/src/core/altium/PcbInspectionReportBuilder.mjs +313 -0
- package/src/core/altium/PcbLayerGroups.mjs +308 -0
- package/src/core/altium/PcbLayerStackCustomDataParser.mjs +183 -0
- package/src/core/altium/PcbLayerStackInterchangeParser.mjs +473 -4
- package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +83 -15
- package/src/core/altium/PcbLayerStackSourceMetadataParser.mjs +74 -4
- package/src/core/altium/PcbLibModelParser.mjs +20 -4
- package/src/core/altium/PcbLibStreamExtractor.mjs +49 -6
- package/src/core/altium/PcbModelParser.mjs +223 -4
- package/src/core/altium/PcbNetMembershipReportBuilder.mjs +270 -0
- package/src/core/altium/PcbStreamExtractor.mjs +130 -6
- package/src/core/altium/PcbTrackPrimitiveParser.mjs +66 -2
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +15 -0
- package/src/core/altium/ProjectHierarchyReportBuilder.mjs +660 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +2 -0
- package/src/core/altium/RawDataPreservationReportBuilder.mjs +348 -0
- package/src/core/altium/SchLibModelParser.mjs +840 -0
- package/src/core/altium/SchLibStreamExtractor.mjs +586 -0
- package/src/core/altium/SchematicBusEntryParser.mjs +3 -2
- package/src/core/altium/SchematicCodeSymbolParser.mjs +663 -0
- package/src/core/altium/SchematicConnectivityQaBuilder.mjs +177 -2
- package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +10 -1
- package/src/core/altium/SchematicFieldCoverageReportBuilder.mjs +549 -0
- package/src/core/altium/SchematicHarnessParser.mjs +9 -3
- package/src/core/altium/SchematicHyperlinkParser.mjs +122 -0
- package/src/core/altium/SchematicNetlistBuilder.mjs +271 -8
- package/src/core/altium/SchematicOwnershipGraphParser.mjs +102 -3
- package/src/core/altium/SchematicPinParser.mjs +12 -45
- package/src/core/altium/SchematicPrimitiveParser.mjs +9 -14
- package/src/core/altium/SchematicQaReportBuilder.mjs +2 -0
- package/src/core/altium/SchematicRecordStreamParser.mjs +183 -0
- package/src/core/altium/SchematicRecordTypeRegistry.mjs +6 -1
- package/src/core/altium/SchematicSheetParser.mjs +8 -2
- package/src/core/altium/SchematicStreamExtractor.mjs +64 -25
- package/src/core/altium/SchematicTextOrientationResolver.mjs +76 -0
- package/src/core/altium/SchematicTextParser.mjs +28 -12
- package/src/core/altium/SchematicTextRunParser.mjs +81 -0
- package/src/core/altium/SchematicThumbnailParser.mjs +425 -0
- package/src/core/altium/UnsupportedFeatureReportBuilder.mjs +380 -0
- package/src/parser.mjs +35 -1
- package/src/renderers.mjs +1 -0
- package/src/ui/SchematicShapeRenderer.mjs +49 -6
- package/src/ui/SchematicSvgRenderer.mjs +37 -8
- package/src/ui/SchematicTypography.mjs +4 -3
|
@@ -10,11 +10,12 @@ export class SchematicConnectivityQaBuilder {
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Builds connectivity QA from the normalized single-sheet net model.
|
|
13
|
-
* @param {{ nets?: object[], texts?: object[], pins?: object[], ports?: object[], junctions?: object[] }} schematic Normalized schematic fragments.
|
|
13
|
+
* @param {{ nets?: object[], texts?: object[], pins?: object[], ports?: object[], junctions?: object[], lines?: object[] }} schematic Normalized schematic fragments.
|
|
14
14
|
* @returns {object}
|
|
15
15
|
*/
|
|
16
16
|
static build(schematic) {
|
|
17
17
|
const nets = Array.isArray(schematic?.nets) ? schematic.nets : []
|
|
18
|
+
const lines = Array.isArray(schematic?.lines) ? schematic.lines : []
|
|
18
19
|
const labels = (schematic?.texts || []).filter(
|
|
19
20
|
(text) => text.recordType === '25'
|
|
20
21
|
)
|
|
@@ -37,6 +38,10 @@ export class SchematicConnectivityQaBuilder {
|
|
|
37
38
|
...SchematicConnectivityQaBuilder.#ambiguousJunctionFindings(
|
|
38
39
|
junctions,
|
|
39
40
|
nets
|
|
41
|
+
),
|
|
42
|
+
...SchematicConnectivityQaBuilder.#unjunctionedTeeContactFindings(
|
|
43
|
+
lines,
|
|
44
|
+
junctions
|
|
40
45
|
)
|
|
41
46
|
]
|
|
42
47
|
|
|
@@ -180,7 +185,12 @@ export class SchematicConnectivityQaBuilder {
|
|
|
180
185
|
* @returns {object}
|
|
181
186
|
*/
|
|
182
187
|
static #summary(nets, findings) {
|
|
183
|
-
|
|
188
|
+
const unjunctionedTeeContactCount =
|
|
189
|
+
SchematicConnectivityQaBuilder.#countCode(
|
|
190
|
+
findings,
|
|
191
|
+
'schematic.connectivity.unjunctioned-tee-contact'
|
|
192
|
+
)
|
|
193
|
+
const summary = {
|
|
184
194
|
netCount: nets.length,
|
|
185
195
|
findingCount: findings.length,
|
|
186
196
|
danglingLabelCount: SchematicConnectivityQaBuilder.#countCode(
|
|
@@ -204,6 +214,98 @@ export class SchematicConnectivityQaBuilder {
|
|
|
204
214
|
'schematic.connectivity.ambiguous-junction'
|
|
205
215
|
)
|
|
206
216
|
}
|
|
217
|
+
|
|
218
|
+
if (unjunctionedTeeContactCount) {
|
|
219
|
+
summary.unjunctionedTeeContactCount = unjunctionedTeeContactCount
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return summary
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Reports segment endpoints that touch another segment interior without an
|
|
227
|
+
* authored junction at the contact point.
|
|
228
|
+
* @param {object[]} lines Normalized line segments.
|
|
229
|
+
* @param {object[]} junctions Authored junctions.
|
|
230
|
+
* @returns {object[]}
|
|
231
|
+
*/
|
|
232
|
+
static #unjunctionedTeeContactFindings(lines, junctions) {
|
|
233
|
+
const findings = []
|
|
234
|
+
const seen = new Set()
|
|
235
|
+
|
|
236
|
+
for (
|
|
237
|
+
let segmentIndex = 0;
|
|
238
|
+
segmentIndex < lines.length;
|
|
239
|
+
segmentIndex += 1
|
|
240
|
+
) {
|
|
241
|
+
const segment = lines[segmentIndex]
|
|
242
|
+
if (!SchematicConnectivityQaBuilder.#isUsableSegment(segment)) {
|
|
243
|
+
continue
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const point of SchematicConnectivityQaBuilder.#segmentEndpoints(
|
|
247
|
+
segment
|
|
248
|
+
)) {
|
|
249
|
+
if (
|
|
250
|
+
junctions.some((junction) =>
|
|
251
|
+
SchematicConnectivityQaBuilder.#samePoint(
|
|
252
|
+
point,
|
|
253
|
+
junction
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
) {
|
|
257
|
+
continue
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for (
|
|
261
|
+
let touchedSegmentIndex = 0;
|
|
262
|
+
touchedSegmentIndex < lines.length;
|
|
263
|
+
touchedSegmentIndex += 1
|
|
264
|
+
) {
|
|
265
|
+
if (touchedSegmentIndex === segmentIndex) {
|
|
266
|
+
continue
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const touchedSegment = lines[touchedSegmentIndex]
|
|
270
|
+
if (
|
|
271
|
+
!SchematicConnectivityQaBuilder.#isUsableSegment(
|
|
272
|
+
touchedSegment
|
|
273
|
+
) ||
|
|
274
|
+
SchematicConnectivityQaBuilder.#isSegmentEndpoint(
|
|
275
|
+
touchedSegment,
|
|
276
|
+
point
|
|
277
|
+
) ||
|
|
278
|
+
!SchematicConnectivityQaBuilder.#segmentContainsPoint(
|
|
279
|
+
touchedSegment,
|
|
280
|
+
point
|
|
281
|
+
)
|
|
282
|
+
) {
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const key =
|
|
287
|
+
SchematicConnectivityQaBuilder.#pointKey(point) +
|
|
288
|
+
':' +
|
|
289
|
+
segmentIndex +
|
|
290
|
+
':' +
|
|
291
|
+
touchedSegmentIndex
|
|
292
|
+
if (seen.has(key)) {
|
|
293
|
+
continue
|
|
294
|
+
}
|
|
295
|
+
seen.add(key)
|
|
296
|
+
findings.push({
|
|
297
|
+
code: 'schematic.connectivity.unjunctioned-tee-contact',
|
|
298
|
+
severity: 'warning',
|
|
299
|
+
x: point.x,
|
|
300
|
+
y: point.y,
|
|
301
|
+
segmentIndex,
|
|
302
|
+
touchedSegmentIndex
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return findings
|
|
207
309
|
}
|
|
208
310
|
|
|
209
311
|
/**
|
|
@@ -256,6 +358,79 @@ export class SchematicConnectivityQaBuilder {
|
|
|
256
358
|
)
|
|
257
359
|
}
|
|
258
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Returns true for a non-degenerate line segment.
|
|
363
|
+
* @param {object} segment Line segment.
|
|
364
|
+
* @returns {boolean}
|
|
365
|
+
*/
|
|
366
|
+
static #isUsableSegment(segment) {
|
|
367
|
+
return !SchematicConnectivityQaBuilder.#samePoint(
|
|
368
|
+
{ x: segment?.x1, y: segment?.y1 },
|
|
369
|
+
{ x: segment?.x2, y: segment?.y2 }
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Lists the two endpoints of a segment.
|
|
375
|
+
* @param {object} segment Line segment.
|
|
376
|
+
* @returns {{ x: number, y: number }[]}
|
|
377
|
+
*/
|
|
378
|
+
static #segmentEndpoints(segment) {
|
|
379
|
+
return [
|
|
380
|
+
{ x: Number(segment.x1), y: Number(segment.y1) },
|
|
381
|
+
{ x: Number(segment.x2), y: Number(segment.y2) }
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Returns true when a point matches either segment endpoint.
|
|
387
|
+
* @param {object} segment Line segment.
|
|
388
|
+
* @param {{ x: number, y: number }} point Test point.
|
|
389
|
+
* @returns {boolean}
|
|
390
|
+
*/
|
|
391
|
+
static #isSegmentEndpoint(segment, point) {
|
|
392
|
+
return SchematicConnectivityQaBuilder.#segmentEndpoints(segment).some(
|
|
393
|
+
(endpoint) =>
|
|
394
|
+
SchematicConnectivityQaBuilder.#samePoint(endpoint, point)
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Returns true when a point lies on a segment.
|
|
400
|
+
* @param {object} segment Line segment.
|
|
401
|
+
* @param {{ x: number, y: number }} point Test point.
|
|
402
|
+
* @returns {boolean}
|
|
403
|
+
*/
|
|
404
|
+
static #segmentContainsPoint(segment, point) {
|
|
405
|
+
const x1 = Number(segment.x1)
|
|
406
|
+
const y1 = Number(segment.y1)
|
|
407
|
+
const x2 = Number(segment.x2)
|
|
408
|
+
const y2 = Number(segment.y2)
|
|
409
|
+
const x = Number(point.x)
|
|
410
|
+
const y = Number(point.y)
|
|
411
|
+
const cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1)
|
|
412
|
+
|
|
413
|
+
if (Math.abs(cross) > 0.01) {
|
|
414
|
+
return false
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
x >= Math.min(x1, x2) - 0.01 &&
|
|
419
|
+
x <= Math.max(x1, x2) + 0.01 &&
|
|
420
|
+
y >= Math.min(y1, y2) - 0.01 &&
|
|
421
|
+
y <= Math.max(y1, y2) + 0.01
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Builds a stable key fragment for one point.
|
|
427
|
+
* @param {{ x: number, y: number }} point Point.
|
|
428
|
+
* @returns {string}
|
|
429
|
+
*/
|
|
430
|
+
static #pointKey(point) {
|
|
431
|
+
return Number(point.x).toFixed(2) + ',' + Number(point.y).toFixed(2)
|
|
432
|
+
}
|
|
433
|
+
|
|
259
434
|
/**
|
|
260
435
|
* Compares points with a small parser-tolerance.
|
|
261
436
|
* @param {object} left First point.
|
|
@@ -62,6 +62,8 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
62
62
|
parseNumericField(componentRecord.fields, 'DisplayModeCount') || 1
|
|
63
63
|
const currentPartId =
|
|
64
64
|
parseNumericField(componentRecord.fields, 'CurrentPartId') || 1
|
|
65
|
+
const activeDisplayMode =
|
|
66
|
+
parseNumericField(componentRecord.fields, 'DisplayMode') || 1
|
|
65
67
|
const partIds = SchematicDisplayModeCatalogParser.#collectPartIds(
|
|
66
68
|
children,
|
|
67
69
|
declaredPartCount
|
|
@@ -88,6 +90,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
88
90
|
SchematicDisplayModeCatalogParser.#partCatalog(
|
|
89
91
|
partId,
|
|
90
92
|
currentPartId,
|
|
93
|
+
activeDisplayMode,
|
|
91
94
|
declaredDisplayModeCount,
|
|
92
95
|
children
|
|
93
96
|
)
|
|
@@ -156,6 +159,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
156
159
|
* Builds one part catalog row.
|
|
157
160
|
* @param {number} partId Native part id.
|
|
158
161
|
* @param {number} currentPartId Active part id.
|
|
162
|
+
* @param {number} activeDisplayMode Active display-mode id.
|
|
159
163
|
* @param {number} declaredDisplayModeCount Declared display-mode count.
|
|
160
164
|
* @param {object[]} children Owner child records.
|
|
161
165
|
* @returns {object}
|
|
@@ -163,6 +167,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
163
167
|
static #partCatalog(
|
|
164
168
|
partId,
|
|
165
169
|
currentPartId,
|
|
170
|
+
activeDisplayMode,
|
|
166
171
|
declaredDisplayModeCount,
|
|
167
172
|
children
|
|
168
173
|
) {
|
|
@@ -185,6 +190,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
185
190
|
displayMode,
|
|
186
191
|
partId,
|
|
187
192
|
currentPartId,
|
|
193
|
+
activeDisplayMode,
|
|
188
194
|
partChildren
|
|
189
195
|
)
|
|
190
196
|
)
|
|
@@ -219,6 +225,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
219
225
|
* @param {number} displayMode Native display-mode id.
|
|
220
226
|
* @param {number} partId Native part id.
|
|
221
227
|
* @param {number} currentPartId Active part id.
|
|
228
|
+
* @param {number} activeDisplayMode Active display-mode id.
|
|
222
229
|
* @param {object[]} partChildren Child records for one part.
|
|
223
230
|
* @returns {object}
|
|
224
231
|
*/
|
|
@@ -226,6 +233,7 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
226
233
|
displayMode,
|
|
227
234
|
partId,
|
|
228
235
|
currentPartId,
|
|
236
|
+
activeDisplayMode,
|
|
229
237
|
partChildren
|
|
230
238
|
) {
|
|
231
239
|
const displayChildren = partChildren.filter(
|
|
@@ -236,7 +244,8 @@ export class SchematicDisplayModeCatalogParser {
|
|
|
236
244
|
|
|
237
245
|
return {
|
|
238
246
|
displayMode,
|
|
239
|
-
isActive:
|
|
247
|
+
isActive:
|
|
248
|
+
partId === currentPartId && displayMode === activeDisplayMode,
|
|
240
249
|
primitiveCount: displayChildren.length,
|
|
241
250
|
pinCount:
|
|
242
251
|
SchematicDisplayModeCatalogParser.#pinCount(displayChildren)
|