Skip to content

Commit

Permalink
Merge branch 'tscircuit:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
AnasSarkiz authored Dec 2, 2024
2 parents d2fcaf5 + 71137e3 commit 3a7f2cd
Show file tree
Hide file tree
Showing 14 changed files with 490 additions and 72 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion lib/components/base-components/NormalComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ export class NormalComponent<
return
}

this._queueAsyncEffect(async () => {
this._queueAsyncEffect("get-supplier-part-numbers", async () => {
this._asyncSupplierPartNumbers = await supplierPartNumbersMaybePromise
this._markDirty("PartsEngineRender")
})
Expand Down
19 changes: 13 additions & 6 deletions lib/components/base-components/Renderable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type RenderPhaseStates = Record<
>

export type AsyncEffect = {
effectName: string
promise: Promise<void>
phase: RenderPhase
complete: boolean
Expand Down Expand Up @@ -101,10 +102,11 @@ export abstract class Renderable implements IRenderable {
}
}

protected _queueAsyncEffect(effect: () => Promise<void>) {
protected _queueAsyncEffect(effectName: string, effect: () => Promise<void>) {
const asyncEffect: AsyncEffect = {
promise: effect(), // TODO don't start effects until end of render cycle
phase: this._currentRenderPhase!,
effectName,
complete: false,
}
this._asyncEffects.push(asyncEffect)
Expand All @@ -116,20 +118,25 @@ export abstract class Renderable implements IRenderable {
// HACK: emit to the root circuit component that an async effect has completed
if ("root" in this && this.root) {
;(this.root as any).emit("asyncEffectComplete", {
component: this,
asyncEffect,
effectName,
componentDisplayName: this.getString(),
phase: asyncEffect.phase,
})
}
})
.catch((error) => {
console.error(`Async effect error in ${asyncEffect.phase}:`, error)
console.error(
`Async effect error in ${asyncEffect.phase} "${effectName}":\n${error.stack}`,
)
asyncEffect.complete = true

// HACK: emit to the root circuit component that an async effect has completed
if ("root" in this && this.root) {
;(this.root as any).emit("asyncEffectComplete", {
component: this,
asyncEffect,
effectName,
componentDisplayName: this.getString(),
phase: asyncEffect.phase,
error: error.toString(),
})
}
})
Expand Down
137 changes: 105 additions & 32 deletions lib/components/primitive-components/Group/Group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { Trace } from "../Trace/Trace"
import { ConnectivityMap } from "circuit-json-to-connectivity-map"
import type { TraceI } from "../Trace/TraceI"
import { getSimpleRouteJsonFromTracesAndDb } from "lib/utils/autorouting/getSimpleRouteJsonFromTracesAndDb"
import Debug from "debug"

export class Group<Props extends z.ZodType<any, any, any> = typeof groupProps>
extends NormalComponent<Props>
Expand Down Expand Up @@ -114,50 +115,121 @@ export class Group<Props extends z.ZodType<any, any, any> = typeof groupProps>
doInitialPcbTraceRender() {
if (this._shouldUseTraceByTraceRouting()) return

let serverUrl = this.props.autorouter?.serverUrl
const serverUrl =
this.props.autorouter?.serverUrl ?? "https://registry-api.tscircuit.com"
const serverMode = this.props.autorouter?.serverMode ?? "job"

if (this.props.autorouter === "auto-cloud") {
// TODO this url should be inferred by scanning parent subcircuits, the
// default should be provided by the platform OR error
serverUrl = "https://registry-api.tscircuit.com/autorouting/solve"
const debug = Debug("tscircuit:core:autorouting")

const fetchWithDebug = (url: string, options: RequestInit) => {
debug("fetching", url)
return fetch(url, options)
}

if (serverUrl) {
// Make a request to the autorouter server
this._queueAsyncEffect(async () => {
// Queue the autorouting request
this._queueAsyncEffect("make-http-autorouting-request", async () => {
if (serverMode === "solve-endpoint") {
// Legacy solve endpoint mode
if (this.props.autorouter?.inputFormat === "simplified") {
const { autorouting_result } = await fetch(serverUrl, {
method: "POST",
body: JSON.stringify({
input_simple_route_json: this._getSimpleRouteJsonFromPcbTraces(),
}),
headers: {
"Content-Type": "application/json",
const { autorouting_result } = await fetchWithDebug(
`${serverUrl}/autorouting/solve`,
{
method: "POST",
body: JSON.stringify({
input_simple_route_json:
this._getSimpleRouteJsonFromPcbTraces(),
}),
headers: { "Content-Type": "application/json" },
},
}).then((r) => r.json())
).then((r) => r.json())
this._asyncAutoroutingResult = autorouting_result
this._markDirty("PcbTraceRender")
return
}

const arRes = await fetch(serverUrl, {
const { autorouting_result } = await fetchWithDebug(
`${serverUrl}/autorouting/solve`,
{
method: "POST",
body: JSON.stringify({
input_circuit_json: this.root!.db.toArray(),
}),
headers: { "Content-Type": "application/json" },
},
).then((r) => r.json())
this._asyncAutoroutingResult = autorouting_result
this._markDirty("PcbTraceRender")
return
}

const { autorouting_job } = await fetchWithDebug(
`${serverUrl}/autorouting/jobs/create`,
{
method: "POST",
body: JSON.stringify({
// TODO filter such that we're only using this subcircuit's
// components
input_circuit_json: this.root!.db.toArray(),
provider: "freerouting",
autostart: true,
}),
headers: {
"Content-Type": "application/json",
headers: { "Content-Type": "application/json" },
},
).then((r) => r.json())

// Poll until job is complete
while (true) {
const { autorouting_job: job } = (await fetchWithDebug(
`${serverUrl}/autorouting/jobs/get`,
{
method: "POST",
body: JSON.stringify({
autorouting_job_id: autorouting_job.autorouting_job_id,
}),
headers: { "Content-Type": "application/json" },
},
})
).then((r) => r.json())) as {
autorouting_job: {
autorouting_job_id: string
is_running: boolean
is_started: boolean
is_finished: boolean
has_error: boolean
error: string | null
autorouting_provider: "freerouting" | "tscircuit"
created_at: string
started_at?: string
finished_at?: string
}
}

const resultText = await arRes.text()
// TODO handle errors
const { autorouting_result } = JSON.parse(resultText)
this._asyncAutoroutingResult = autorouting_result
this._markDirty("PcbTraceRender")
})
}
if (job.is_finished) {
const { autorouting_job_output } = await fetchWithDebug(
`${serverUrl}/autorouting/jobs/get_output`,
{
method: "POST",
body: JSON.stringify({
autorouting_job_id: autorouting_job.autorouting_job_id,
}),
headers: { "Content-Type": "application/json" },
},
).then((r) => r.json())

this._asyncAutoroutingResult = {
output_pcb_traces: autorouting_job_output.output_pcb_traces,
}
this._markDirty("PcbTraceRender")
break
}

if (job.has_error) {
throw new Error(
`Autorouting job failed: ${JSON.stringify(job.error)}`,
)
}

// Wait before polling again
await new Promise((resolve) => setTimeout(resolve, 100))
}
})
}

updatePcbTraceRender() {
Expand Down Expand Up @@ -284,9 +356,10 @@ export class Group<Props extends z.ZodType<any, any, any> = typeof groupProps>
* or if using a "fullview" or "rip and replace" autorouting mode
*/
_shouldUseTraceByTraceRouting(): boolean {
// HACK: change when @tscircuit/props provides a spec for the autorouter
// prop
if (this.props.autorouter) return false
const props = this._parsedProps as SubcircuitGroupProps
if (props.autorouter === "auto-local") return true
if (props.autorouter === "sequential-trace") return true
if (props.autorouter) return false
return true
}
}
15 changes: 10 additions & 5 deletions lib/hooks/create-use-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,20 @@ export const createUseComponent: CreateUseComponentConstPinLabels &
} else if (typeof pins === "object") {
pinLabelsFlatArray.push(
...Object.values(pins as Record<string, string[]>).flat(),
...(Object.keys(pins) as string[]),
)
pinLabelsFlatArray.push(...(Object.keys(pins) as string[]))
}
const R: any = (props2: any) => {
const combinedProps = { ...props, ...props2, name }
const tracesToCreate: any[] = []
// Explicitly throw an error if names don't match
if (props2?.name && props2.name !== name) {
throw new Error(
`Component name mismatch. Hook name: ${name}, ` +
`Component prop name: ${props2.name}`,
)
}

const combinedProps = { ...props, ...props2, name: name }
const tracesToCreate: any[] = []
for (const portLabel of pinLabelsFlatArray) {
if (combinedProps[portLabel]) {
const from = `.${name} > .${portLabel}`
Expand All @@ -71,7 +78,6 @@ export const createUseComponent: CreateUseComponentConstPinLabels &
delete combinedProps[portLabel]
}
}

return (
<>
<Component {...combinedProps} />
Expand All @@ -85,7 +91,6 @@ export const createUseComponent: CreateUseComponentConstPinLabels &
for (const port of pinLabelsFlatArray) {
R[port] = `.${name} > .${port}`
}

return R
}
}
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@tscircuit/core",
"type": "module",
"version": "0.0.208",
"version": "0.0.211",
"types": "dist/index.d.ts",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down Expand Up @@ -41,11 +41,11 @@
"@tscircuit/footprinter": "^0.0.89",
"@tscircuit/infgrid-ijump-astar": "^0.0.24",
"@tscircuit/math-utils": "^0.0.5",
"circuit-json": "^0.0.108",
"circuit-json-to-connectivity-map": "^0.0.17",
"@tscircuit/props": "^0.0.106",
"@tscircuit/props": "^0.0.107",
"@tscircuit/schematic-autolayout": "^0.0.6",
"@tscircuit/soup-util": "^0.0.41",
"circuit-json": "^0.0.108",
"circuit-json-to-connectivity-map": "^0.0.17",
"circuit-to-svg": "0.0.84",
"format-si-unit": "^0.0.2",
"nanoid": "^5.0.7",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class TestComponent extends Renderable {

doInitialSourceRender() {
// Test async effect
this._queueAsyncEffect(async () => {
this._queueAsyncEffect("test-async-effect", async () => {
await new Promise((resolve) => setTimeout(resolve, 100))
})
}
Expand Down
Loading

0 comments on commit 3a7f2cd

Please sign in to comment.