app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
Rename FUSION_3D_FINDINGS.md -> LIBRARY_FINDINGS.md (it's library-into-Fusion, not broad 3D); update all refs; mark the 3D attach SOLVED via fusion_attach_3d_package in the skill
4 files changed
+214−7
LIBRARY_FINDINGS.mdadded+202@@ -0,0 +1,202 @@+# Getting component libraries into Fusion - findings log++Hard-won findings from cracking "attach the real 3D chip to a Fusion library package." Captured as we+go so the final MAKING_LIBRARIES.md + fusion-libraries skill are complete and correct. Newest insight+at the bottom of each section.++## 1. Use chip-fetcher's curated LIBRARY, not a raw /tmp download++`chip-fetcher/library/<MPN>/` is curated and carries **variant-specific** files. For Fusion, the right+inputs are the **`.fusion`** variant (generated by Adom's `adom-sfconvert`), paired with the curated+named 3D:++| File | Use |+|---|---|+| `<MPN>.fusion.kicad_sym` | symbol, Fusion-tuned -> feed to adom-lbr |+| `<MPN>.fusion.kicad_mod` | footprint, Fusion-tuned -> feed to adom-lbr |+| `<MPN>-named.step` | the curated 3D (part number etched on top; oriented). Pair this with the footprint |+| `<MPN>.kicad_mod` / `.manufacturer.*` / `.altium.*` | raw / other-EDA variants, NOT for Fusion |++MISTAKE made + fixed: I first built the `.lbr` from a raw `/tmp/adom-chip-fetcher-*/EAGLE/*.lbr` and+the thin 245KB `manufacturer.step`. That gave a footprint/3D size mismatch. Switching to+`.fusion.kicad_mod` + `-named.step` is the fix. (Convention confirmed in `chip-fetcher/src/thumbnails.rs`:+it builds the lbr with `--name <mpn>.fusion` from the `.fusion` files.)++## 2. adom-lbr is the right tool; pass it the .fusion files++`adom-lbr generate --sym <MPN>.fusion.kicad_sym --fp <MPN>.fusion.kicad_mod -o <MPN>.lbr --name <MPN>`+then `adom-lbr validate <MPN>.lbr`. adom-lbr has no 3D/URN flag (it is symbol+footprint only); the 3D+is attached separately in Fusion (sections 4-6). The VENDOR EAGLE `.lbr` must NOT be fed to+`Create3DPackage` - it fails Fusion's XML parser ("File doesn't contain valid XML data"); an+adom-lbr-generated `.lbr` parses fine.++## 3. Getting the .lbr / .step onto Windows++The pipeline runs in the cloud container; Fusion runs on Windows; there is NO container->Windows push+verb. Serve the files (`python3 -m http.server 8931`) and pull them with `shell_execute` ++`Invoke-WebRequest -OutFile C:\Users\<user>\adom-lib\...`. Build the shell JSON with `json.dumps`+(Windows paths need `\\`).++## 4. Opening the footprint in the 3D viewer (Package3DEnvironment)++- `fusion_open_lbr` the `.lbr` so the **library document is ACTIVE** (required).+- `fusion_execute_text_command {"command":"Electron.Create3DPackage C:/.../<MPN>.lbr"}` opens the+ **`Package3DEnvironment`** workspace showing the footprint (green courtyard + pads + `>NAME`/`>VALUE`).+ ERROR if the library is not active: "Please run the command in library document."+- Command defs in that env: `Package3DCmd`, `Package3DCreateCmd`, `Package3DStop` (Stop = FINISH).++## 5. Placing the real 3D chip via the API (works)++Inside the env, with `fusion_run_modeling_script`:+```python+d = adsk.fusion.Design.cast(app.activeProduct); root = d.rootComponent+im = app.importManager+im.importToTarget(im.createSTEPImportOptions("C:/.../<MPN>-named.step"), root) # the real body+oc = root.occurrences.item(root.occurrences.count-1)+# auto-orient: rotate the smallest bbox dimension to Z so it lies flat+# (named.step is usually already Z-thin; raw STEP often imports Y-up and needs +90 about X)+```+The chip imports at correct scale and sits on the footprint.++## 6. The library-context commands (the proper in-library path)++Enumerating command defs while the **library** is active surfaced the real in-library commands:+- `Electron::AddCustom3DModel` - opens a design to add a custom 3D model to the selected package.+- `AttachToFootprintCmd` - attaches the modeled body to the footprint (this is what maps it back ++ mints the eagle URN). It is INTERACTIVE (holds the main thread waiting for a footprint-align confirm),+ so it resists pure-API completion; recover with Escape if it wedges.+- also: `Electron::EditCustom3DModel`, `Electron::PackageFromWeb`, `Electron::RemoveCustom3DModel`.++## 7. THE URN SCHEME (the crux)++A library `.lbr` references a 3D package by **`urn:adsk.eagle:package:NNNNN/V`** (managed-library scheme),+NOT the **`urn:adsk.wipprod:...`** scheme that `fusion_aps_upload` / `save_to_cloud` produce. So you+cannot just paste an uploaded-STEP URN into the `.lbr`. The eagle URN is minted only when Fusion's 3D+Package Generator FINISHes mapped to the package. The `.lbr` markup (from the EAGLE spec + Autodesk+forum): `<packages3d><package3d urn="urn:adsk.eagle:package:N/V" type="model">...</package3d></packages3d>`+and the device carries `override_package3d_urn` / a `<package3dinstance package3d_urn="...">`.++## 8. Open item++The one step not yet automated end-to-end: minting + mapping the eagle URN (the FINISH inside the+in-library generator). Current plan: open the footprint env with the chip placed (sections 4-5), let+the human align + FINISH/save once, then export that `.lbr`, read the exact `package3d` markup + eagle+URN, and from there automate the edit-and-reupload for every future chip.++## 9. Integration test (adom-lbr.fusion3d/1 sidecar) - results++Tested the adom-lbr-fixed `.lbr` + `ads.fusion3d.json` sidecar end to end:+- **Footprint writer fix: VERIFIED.** The new `adom-lbr generate` emits a real 4-sided LQFP-64+ (16 left / 16 right / 16 top / 16 bottom `<smd>`, X & Y ±5.74 mm), 64 `<connect>` intact. Opens in+ Fusion as a proper QFP, not a 2-column placeholder. (Format nit: the `<smd>` have **no `rot=`** - fine+ for square pads, but non-square pads on the top/bottom sides would need `rot="R90"`.)+- **URN minting: the assumed scheme is WRONG.** Running the 3D Package Generator+ (`Create3DPackage` -> import model -> `Package3DStop`/FINISH -> Save) mints+ **`urn:adsk.wipprod:dm.lineage:...`** (a regular user cloud design), NOT the+ **`urn:adsk.eagle:package:NNN/V`** the manifest expects. Fusion's generator saves a *custom* 3D model+ as an ordinary cloud file (wipprod), not a managed eagle package.+- **Binding the wipprod URN: FAILS.** Writing `<packages3d><package3d urn="urn:adsk.wipprod:dm.lineage:...">`+ + a device `<package3dinstance package3d_urn="...">` into the `.lbr` and reopening gives+ **"<file>.lbr has errors and cannot be opened. Close the empty design file..."** So a wipprod URN is+ not valid in `package3d`; the `.lbr` genuinely needs an `adsk.eagle:package` URN.+- **THE REMAINING BLOCKER:** how to mint an `adsk.eagle:package` URN for a custom model. The standalone+ generator path does not produce one. The in-library `Electron::AddCustom3DModel` + `AttachToFootprintCmd`+ path (section 6) is the likely minter, but it is interactive. This is the crux to crack next.++## 10. Capturing GUI error dialogs (Fusion) - ALWAYS check owned popups++Fusion error dialogs (e.g. "<file>.lbr has errors and cannot be opened") are **owned popups**: they do+NOT enumerate in `desktop_list_windows` and are invisible to a plain capture of the main window. A+`fusion_*` verb can return `success:true` while an error dialog is actually up.++**`desktop_screenshot_window {hwnd}` (AD v1.8.177+) captures them automatically** and returns them at the+TOP LEVEL: **`ownedPopupCount`** (always present; 0 = none) + **`_screenshots[]`** (each owned popup:+`title`, `kind:"owned_popup"`, `localSafePath`, `rect`, `coordMap`). The CLI auto-pulls each popup PNG.++RULE: after every Fusion open / `execute_text_command` / generator FINISH, grab+`desktop_screenshot_window` on the Fusion main hwnd and **check `ownedPopupCount` / read the+`screenshots[]` array** (each entry: `title`, `localSafePath`, `coordMap`, `shotId`; the CLI auto-pulls+each PNG to `/tmp/ad-shots/`). Do NOT just read `output.data.image` (that is the main window only and+misses owned dialogs). Treat `ownedPopupCount > 0` as "a dialog is up - read each popup before proceeding."+(Field note: `_screenshots`/`_popups_note` are doc-strings; the real array is `screenshots`.)++## 11. SUCCESS - the WORKING 3D-attach flow (supersedes the section 9 "bind fails" path)++The manual `package3d`-XML / pre-minted-URN approach (section 9) was a dead end. The **generator FINISH,+launched from the open library, DOES bind the 3D to the deviceset** - no XML editing, no wipprod URN.+Proven end-to-end on ADS8588SIPM (LQFP-64): the Content Manager tree shows the 3D nested under the+package under the deviceset, the Packages panel's `Package` column flips from `Placeholder` to the part+name, and the 3D preview becomes the real chip. **This is the first time the full Fusion electronics 3D+attach has been automated through the bridge.**++The repeatable sequence (all via existing verbs):+1. Build the `.lbr` from the chip-fetcher **`.fusion`** variant (`adom-lbr generate`); footprint must be a+ real 4-sided package (the writer fix). Stage `.lbr` + the model STEP to Windows.+2. `fusion_open_lbr` the `.lbr` so the **library is the ACTIVE document**.+3. `fusion_execute_text_command Electron.Create3DPackage <C:/...lbr>` -> opens `Package3DEnvironment`+ showing the footprint.+4. `fusion_run_modeling_script`: `importManager.importToTarget(createSTEPImportOptions(<C:/...step>), root)`,+ then auto-orient (rotate the smallest bbox dim to Z so it lies flat on the footprint).+5. `fusion_run_modeling_script`: `ui.commandDefinitions.itemById("Package3DStop").execute()` (FINISH).+6. A **Save** dialog appears (owned dialog) -> `desktop_ui_click` its+ `QTApplication.QTFrameWindow.standardActions.SaveButton`.+7. Back on the library: the deviceset's package now carries the real 3D. **The right-panel preview LAGS** -+ re-grab the window (or click the package in the Content Manager) to see `Package`=part-name + the real+ 3D; the tree is the source of truth, the preview refresh is delayed.++PITFALL corrected: do NOT conclude "it didn't bind" from a stale gray preview right after FINISH - check+the Content Manager tree / the `Package` column, and re-grab after a moment.++## 12. GROUND TRUTH - exactly how Fusion bakes the 3D URN into the .lbr++Exported the bound library to `.flbr` (`fusion_export_source` with a `.flbr` extension), which is a+Fusion document ZIP; the EAGLE library XML lives in `.../electron.BlobParts/ExtFile<uuid>` (24 KB,+`<eagle version="9.7.0">`). It DOES contain the 3D, and here is the exact markup Fusion writes for a+CUSTOM (user-uploaded) 3D model:++```xml+<packages3d>+ <package3d name="ADS8588SIPM" urn="" wip_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"+ locally_modified="yes" type="model">+ <description>ADS8588SIPM</description>+ <packageinstances><packageinstance name="ADS8588SIPM"/></packageinstances>+ </package3d>+</packages3d>+<!-- and inside the matching <device ...>: -->+<package3dinstances>+ <package3dinstance package3d_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"/>+</package3dinstances>+```++THE DELTA (why the section-9 manual bind failed):+- `urn=""` is **EMPTY** for a custom model (the `urn` slot is for MANAGED `urn:adsk.eagle:package:N/V`+ content only). The actual reference goes in **`wip_urn=`**.+- The URN form is the **versioned file URN** `urn:adsk.wipprod:fs.file:vf.<id>?version=1`, NOT the+ `dm.lineage` form. (`fusion_aps_upload`/`save_to_cloud` return a `dm.lineage` lineage URN; the+ `?version=1` file URN is the one to use - get it from `fusion_aps_file_info`/the saved version.)+- Add **`locally_modified="yes"`** and `type="model"`.+- The device's `<package3dinstance package3d_urn="...">` uses the SAME `fs.file:vf...?version=1` form.++So the manifest's `adsk.eagle:package` assumption is only for managed-library parts; for a+self-uploaded model the binding is a `wip_urn` (fs.file:vf) with empty `urn`. With this exact shape,+adom-lbr (or the bridge) CAN write the binding directly given the model's versioned file URN - no+generator UI needed. (The generator FINISH path also works and is what produced this reference.)++## 13. Resolved sidecar contract (answers to adom-lbr v1.1.1)++**Q: what does the sidecar ship for the FINISH path?** Just `model.path` + `package` + `placement`.+That is sufficient - the bridge does Create3DPackage -> import -> orient -> FINISH -> Save. `placement`+stays useful as an orientation hint/override (the bridge's auto-orient is "rotate smallest bbox dim to Z";+a known pin-1 rotation/offset in `placement` lets adom-lbr override it when a part needs it).++**Q: write the minted wip_urn back to the sidecar (status -> bound)?** YES, record it - for idempotency+(re-runs detect "already bound"), provenance (which model version), and to enable the offline `bind-3d`+fast-path later. BUT the **in-Fusion library is the runtime source of truth**; the sidecar record is+bookkeeping that **adom-lbr owns + writes**. The bridge's part is to REPORT the minted URN: after the+Save, `fusion_aps_file_info {query:"<package>"}` returns the saved 3D-package design's tip+**`fs.file:vf.<id>?version=N`** URN - adom-lbr writes that into the sidecar (`urn`, `urn_status:"bound"`).++**Q: dm.lineage vs fs.file in the .lbr?** ONLY the versioned **`fs.file:vf.<id>?version=N`** goes in the+`.lbr` - in both `wip_urn=` and the device `package3d_urn=`. NEVER `dm.lineage` in the `.lbr` (Fusion pins+a specific version, consistent with `locally_modified="yes"`). `dm.lineage` is the stable cross-version ID;+keep it (optionally) only as SIDECAR provenance for re-resolving the latest version, never in the binding.
MAKING_LIBRARIES.md+1−1@@ -129,7 +129,7 @@ Export the bound library (`fusion_export_source` to a `.flbr` ZIP; the EAGLE XML Key: `urn=""` is empty (that slot is for managed `urn:adsk.eagle:package` parts only); the real link is **`wip_urn=`** with the **versioned file URN** `urn:adsk.wipprod:fs.file:vf.<id>?version=1` (NOT the `dm.lineage` form), plus `locally_modified="yes"`. With this exact shape the binding can be written-directly given the model's versioned file URN. Full detail: [FUSION_3D_FINDINGS.md](FUSION_3D_FINDINGS.md) §11-12.+directly given the model's versioned file URN. Full detail: [LIBRARY_FINDINGS.md](LIBRARY_FINDINGS.md) §11-12. ## Pitfalls
server.py+2−2@@ -1010,7 +1010,7 @@ def _orchestrate_open_lbr(args: dict) -> dict: "ownedPopupCount>0 means an error/confirm dialog is up. " "NOTE: an adom-lbr .lbr is 2D ONLY - symbol + footprint + a PLACEHOLDER 3D package. " "To attach the real 3D chip, use fusion_attach_3d_package (it runs the Package3D generator + "- "FINISH, which binds the 3D onto the deviceset). See the 'fusion-libraries' skill / FUSION_3D_FINDINGS.md."+ "FINISH, which binds the 3D onto the deviceset). See the 'fusion-libraries' skill / LIBRARY_FINDINGS.md." ), } if symbol_name:@@ -1093,7 +1093,7 @@ def _orchestrate_attach_3d_package(args: dict) -> dict: "{hwnd, automationId:'QTApplication.QTFrameWindow.standardActions.SaveButton'}. " "THEN VERIFY with desktop_screenshot_window on the Fusion hwnd: check ownedPopupCount (errors are owned " "popups), and the deviceset's Package column should flip Placeholder->part-name + the 3D preview becomes "- "the real chip (the preview LAGS a beat - re-grab). Full flow: fusion-libraries skill / FUSION_3D_FINDINGS.md sect 11."+ "the real chip (the preview LAGS a beat - re-grab). Full flow: fusion-libraries skill / LIBRARY_FINDINGS.md sect 11." ), }
skills/fusion-libraries/SKILL.md+9−4@@ -33,7 +33,7 @@ chip-fetcher -> adom-symbol -> .kicad_sym ┐ (OpenCASCADE relief on the top face), driven by `chip-fetcher/scripts/occt-name-on-chip.py`. - **fusion_open_lbr** imports the `.lbr` into Fusion's Electronics Library editor. -See **[FUSION_3D_FINDINGS.md](../../FUSION_3D_FINDINGS.md)** for the full live findings log.+See **[LIBRARY_FINDINGS.md](../../LIBRARY_FINDINGS.md)** for the full live findings log. ## 2. Opening a .lbr in Fusion (verified working) @@ -71,9 +71,14 @@ urn:adsk.wipprod:fs.file:vf.<id>?version=<N> 2. **Open the generator.** `fusion_execute_text_command {"command":"Electron.Create3DPackage C:/.../<MPN>.lbr"}` opens the **3D PACKAGE Generator**: a Design-workspace doc with the footprint loaded and a PACKAGE / SELECT / FINISH ribbon. 3. **Insert model + FINISH** -> Fusion writes the `package3d` URN back to the library. -**What is NOT solved yet (the last mile):**-- Step 3's insert + FINISH are **UI-only**: no text command (`fusion_list_text_commands` has `Create3DPackage` but nothing to insert a model or finish) and no routed verb. Completing it today needs either Qt-ribbon UI driving (UIA does not surface those collapsed menu items cleanly) or a **new bridge add-in command (`fusion_attach_3d_package`)** that does the association through Fusion's Library/Package API. That verb is the next build.-- There is still **no on-disk example** of a `.lbr` with a real `package3d` URN (not even vendor EAGLE downloads carry 3D); the markup is generated by the FINISH, so reverse-engineer it from one real FINISH before writing any by hand.+**SOLVED - use `fusion_attach_3d_package` (shipped in v1.5.7):**+One verb does steps 2-3 end to end: open the `.lbr` (library active) -> `Create3DPackage` -> import the+STEP + auto-orient -> `Package3DStop` (FINISH). It returns a `_hint` with the one desktop step left: click+the Fusion **Save** dialog via `desktop_ui_click {automationId:'QTApplication.QTFrameWindow.standardActions.SaveButton'}`.+The 3D then binds onto the deviceset (Content Manager tree shows it; the `Package` column flips+Placeholder -> part name; the preview LAGS a beat). Fusion writes this exact markup (for a self-uploaded+model): `<package3d urn="" wip_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1" locally_modified="yes" type="model">`++ a device `<package3dinstance package3d_urn="...fs.file:vf...?version=1"/>`. See LIBRARY_FINDINGS.md §11-12. Uploading models into the user's Fusion cloud is an outward, persistent action - confirm first, and put uploads in a contained folder (we used `Standard Components / Electronics 3D`).