Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/components/normal-components/Connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import type { AnyCircuitElement, SourceSimpleConnector } from "circuit-json"
import { unknown_error_finding_part } from "circuit-json"
import { createComponentsFromCircuitJson } from "lib/utils/createComponentsFromCircuitJson"
import { rewriteToStandardUsbCPortHints } from "lib/utils/connectors/rewriteToStandardUsbCPortHints"
import { convertCircuitJsonToUsbCStandardCircuitJson } from "lib/utils/connectors/convertCircuitJsonToUsbCStandardCircuitJson"
import { symbols } from "schematic-symbols"
import { Chip } from "./Chip"
import { insertInnerSymbolInSchematicBox } from "./Connector_insertInnerSymbolInSchematicBox"
Expand Down Expand Up @@ -100,12 +100,12 @@ export class Connector<
standard: string,
circuitJson: AnyCircuitElement[],
): void {
const rewrittenCircuitJson =
const props = this._getConnectorProps()
const standardizedCircuitJson =
standard === "usb_c"
? rewriteToStandardUsbCPortHints(circuitJson)
? convertCircuitJsonToUsbCStandardCircuitJson(circuitJson)
: circuitJson

const props = this._getConnectorProps()
const fpComponents = createComponentsFromCircuitJson(
{
componentName: this.name,
Expand All @@ -114,7 +114,7 @@ export class Connector<
pinLabels: props.pinLabels,
pcbPinLabels: props.pcbPinLabels,
},
rewrittenCircuitJson,
standardizedCircuitJson,
)
this.addAll(fpComponents)
this._markDirty("InitializePortsFromChildren")
Expand Down
103 changes: 103 additions & 0 deletions lib/utils/connectors/convertCircuitJsonToUsbCStandardCircuitJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import type { AnyCircuitElement } from "circuit-json"

/**
* Canonical USB-C pin labels every USB-C connector schematic should expose.
* The order matters: earlier labels get first pick when multiple unassigned
* ports could match.
*/
const STANDARD_USB_C_PIN_LABELS: Array<{ label: string; aliases: string[] }> = [
{ label: "GND1", aliases: [] },
{ label: "VBUS1", aliases: [] },
{ label: "CC1", aliases: [] },
{ label: "DP1", aliases: [] },
{ label: "DM1", aliases: ["DN1"] },
{ label: "SBU1", aliases: [] },
{ label: "SBU2", aliases: [] },
{ label: "DM2", aliases: ["DN2"] },
{ label: "DP2", aliases: [] },
{ label: "CC2", aliases: [] },
{ label: "VBUS2", aliases: [] },
{ label: "GND2", aliases: [] },
{ label: "SHELL1", aliases: ["MH1", "EH1", "MOUNT1"] },
{ label: "SHELL2", aliases: ["MH2", "EH2", "MOUNT2"] },
{ label: "SHELL3", aliases: ["MH3", "EH3", "MOUNT3"] },
{ label: "SHELL4", aliases: ["MH4", "EH4", "MOUNT4"] },
]

const PIN_NUMBER_HINT_PATTERN = /^(?:pin)?(\d+)$/i
const dedupeHintsPreservingOrder = (hints: string[]): string[] =>
Array.from(new Set(hints))

/**
* Convert part circuit JSON into USB-C-standardized circuit JSON by appending
* canonical USB-C labels to `port_hints` while preserving original hints and
* pin numbers.
*/
export const convertCircuitJsonToUsbCStandardCircuitJson = (
partCircuitJson: AnyCircuitElement[],
): AnyCircuitElement[] => {
const unassignedPorts: Array<{
pinKey: `pin${number}`
upperCaseHints: Set<string>
}> = []
for (const elm of partCircuitJson) {
if (elm.type !== "source_port") continue
const pinNumber = elm.pin_number
if (typeof pinNumber !== "number") continue
const upperCaseHints = new Set<string>()
for (const hint of elm.port_hints ?? []) {
upperCaseHints.add(hint.trim().toUpperCase())
}
unassignedPorts.push({ pinKey: `pin${pinNumber}`, upperCaseHints })
}

const canonicalHintsByPin: Record<`pin${number}`, string[]> = {}
for (const { label, aliases } of STANDARD_USB_C_PIN_LABELS) {
const canonicalAndAliasHintsUpper = [label, ...aliases].map((s) =>
s.toUpperCase(),
)
const matchIndex = unassignedPorts.findIndex((port) =>
canonicalAndAliasHintsUpper.some((hint) => port.upperCaseHints.has(hint)),
)
if (matchIndex === -1) continue
const { pinKey } = unassignedPorts[matchIndex]
canonicalHintsByPin[pinKey] = [label]
unassignedPorts.splice(matchIndex, 1)
}

if (Object.keys(canonicalHintsByPin).length === 0) return partCircuitJson

return partCircuitJson.map((elm) => {
if (!("port_hints" in elm) || !Array.isArray(elm.port_hints)) return elm

const originalHints = elm.port_hints
.filter((h): h is string => typeof h === "string")
.map((h) => h.trim())
.filter((h) => h.length > 0)
if (originalHints.length === 0) return elm

const pinKeys = new Set<`pin${number}`>()
if (elm.type === "source_port" && typeof elm.pin_number === "number") {
pinKeys.add(`pin${elm.pin_number}`)
}
for (const hint of originalHints) {
const matchedPin = hint.match(PIN_NUMBER_HINT_PATTERN)
if (!matchedPin) continue
pinKeys.add(`pin${Number.parseInt(matchedPin[1], 10)}`)
}

const canonicalHintsToAdd: string[] = []
for (const pinKey of pinKeys) {
canonicalHintsToAdd.push(...(canonicalHintsByPin[pinKey] ?? []))
}
if (canonicalHintsToAdd.length === 0) return elm

return {
...elm,
port_hints: dedupeHintsPreservingOrder([
...originalHints,
...canonicalHintsToAdd,
]),
}
})
}
121 changes: 0 additions & 121 deletions lib/utils/connectors/rewriteToStandardUsbCPortHints.ts

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { AnyCircuitElement } from "circuit-json"
import { getTestFixture } from "tests/fixtures/get-test-fixture"
import usbCC165948CircuitJson from "tests/fixtures/assets/usb-c-C165948.circuit.json"

test("connector usb_c adds DM aliases without remapping pin numbers or shell labels", async () => {
test("connector usb_c derives standard pin labels without remapping pin numbers", async () => {
const { circuit } = getTestFixture()

const mockPartsEngine: PartsEngine = {
Expand Down Expand Up @@ -45,27 +45,21 @@ test("connector usb_c adds DM aliases without remapping pin numbers or shell lab
sp.source_component_id === sourceComponent!.source_component_id,
)

const dn1Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN1"))
const dn2Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN2"))
const gnd1Port = sourcePorts.find((sp: any) =>
sp.port_hints?.includes("GND1"),
)
const vbus1Port = sourcePorts.find((sp: any) =>
sp.port_hints?.includes("VBUS1"),
)
const portByLabel = (label: string) =>
sourcePorts.find((sp: any) => sp.port_hints?.includes(label))

expect(dn1Port?.port_hints).toContain("DM1")
expect(dn2Port?.port_hints).toContain("DM2")
// DN1/DN2 from the raw JSON get canonicalized to DM1/DM2.
expect(portByLabel("DM1")).toBeTruthy()
expect(portByLabel("DM2")).toBeTruthy()

// C165948 keeps these on pin13/pin15; normalization should not remap numbers.
expect(gnd1Port?.pin_number).toBe(13)
expect(vbus1Port?.pin_number).toBe(15)
// C165948 keeps these signals on pin13/pin15; resolution must not remap
// manufacturer pin numbers.
expect(portByLabel("GND1")?.pin_number).toBe(13)
expect(portByLabel("VBUS1")?.pin_number).toBe(15)

// We preserve EH aliases and do not introduce shell canonical labels.
expect(sourcePorts.some((sp: any) => sp.port_hints?.includes("EH1"))).toBe(
true,
)
expect(sourcePorts.some((sp: any) => sp.port_hints?.includes("SHELL1"))).toBe(
false,
)
// Standard shell labels are resolved against the part's EH* mounting pins.
expect(portByLabel("SHELL1")).toBeTruthy()
expect(portByLabel("SHELL2")).toBeTruthy()
expect(portByLabel("SHELL3")).toBeTruthy()
expect(portByLabel("SHELL4")).toBeTruthy()
})
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,15 @@ test("connector usb_c falls back to manufacturerPartNumber when findPart returns
)
const hasHint = (hint: string) =>
sourcePorts.some((sp: any) => sp.port_hints?.includes(hint))
const dn1Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN1"))
const dn2Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN2"))

expect(hasHint("CC1")).toBe(true)
expect(hasHint("CC2")).toBe(true)
expect(hasHint("GND1")).toBe(true)
expect(hasHint("GND2")).toBe(true)
expect(hasHint("VBUS1")).toBe(true)
expect(hasHint("VBUS2")).toBe(true)
expect(dn1Port?.port_hints).toContain("DM1")
expect(dn2Port?.port_hints).toContain("DM2")
expect(hasHint("DM1")).toBe(true)
expect(hasHint("DM2")).toBe(true)

const pads = circuit.db.pcb_smtpad.list()
expect(pads.length).toBeGreaterThan(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,22 @@ test("connector with standard='usb_c' fetches circuit json from parts engine", a
)
const hasHint = (hint: string) =>
sourcePorts.some((sp: any) => sp.port_hints?.includes(hint))
const dn1Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN1"))
const dn2Port = sourcePorts.find((sp: any) => sp.port_hints?.includes("DN2"))

// Standard USB-C pin labels should be derived from the fetched circuit json
// and applied to the corresponding ports regardless of manufacturer pin
// numbering.
expect(hasHint("CC1")).toBe(true)
expect(hasHint("CC2")).toBe(true)
expect(hasHint("GND1")).toBe(true)
expect(hasHint("GND2")).toBe(true)
expect(hasHint("VBUS1")).toBe(true)
expect(hasHint("VBUS2")).toBe(true)
expect(dn1Port?.port_hints).toContain("DM1")
expect(dn2Port?.port_hints).toContain("DM2")
expect(hasHint("DM1")).toBe(true)
expect(hasHint("DM2")).toBe(true)
expect(hasHint("DP1")).toBe(true)
expect(hasHint("DP2")).toBe(true)
expect(hasHint("SBU1")).toBe(true)
expect(hasHint("SBU2")).toBe(true)

// Verify footprint pads were added from the fetched circuit JSON
const pads = circuit.db.pcb_smtpad.list()
Expand Down
Loading
Loading