← Commit history

v1.9.3 source: BOM collapse transparency (collapsedUnits + possibleUnderCollapse), add-in 1.0.5

John Lauer ·60920a4e54 ·1mo ago ·parent 1e1dbac
5 files changed +53−9
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.2\ No newline at end of file+1.9.3\ No newline at end of file
addin/AdomBridge/AdomBridge.manifest+1−1
@@ -6,7 +6,7 @@     "description": {         "": "Adom Desktop Conduit bridge add-in. Exposes a localhost HTTP API for external automation of Fusion 360."     },-    "version": "1.0.4",+    "version": "1.0.5",     "runOnStartup": true,     "supportedOS": "windows" }\ No newline at end of file
addin/AdomBridge/commands/assembly_bom.py+46−2
@@ -56,13 +56,27 @@ def handle_assembly_bom(app: adsk.core.Application, args: dict) -> dict:     exclude = tuple(args.get("exclude") or [])     include_pp = bool(args.get("includePhysicalProperties", False)) -    def is_leaf(comp):+    def kit_rule(comp):         n = comp.name.lower()-        if any(k in n for k in kit):+        for k in kit:+            if k in n:+                return k+        return None++    def is_leaf(comp):+        if kit_rule(comp) is not None:             return True         return comp.bRepBodies.count > 0  # a physical part is a leaf      parts = {}+    # Transparency (issue #25, Oliver + John's call): every heuristic collapse is REPORTED so a+    # name-match on someone's own "bearing test jig" is a visible, correctable decision instead of+    # silently vanishing its internals from the BOM. collapsed = {unitName: {rule, quantity}}.+    collapsed = {}+    # Attribution for the inverse advisory: which DESCENDED subassembly each leaf came from, so a+    # purchased-looking unit that was exploded (240 identical bearing balls) can be flagged.+    sub_stats = {}   # subassembly name -> {part name -> instances}+    sub_stack = []      def walk(occs):         for i in range(occs.count):@@ -74,6 +88,13 @@ def handle_assembly_bom(app: adsk.core.Application, args: dict) -> dict:             children = occ.childOccurrences             has_children = children is not None and children.count > 0             if is_leaf(comp):+                rule = kit_rule(comp)+                if rule is not None and has_children:+                    c = collapsed.setdefault(name, {"unit": name, "matchedRule": rule, "quantity": 0})+                    c["quantity"] += 1+                if sub_stack and rule is None:+                    stats = sub_stats.setdefault(sub_stack[-1], {})+                    stats[name] = stats.get(name, 0) + 1                 row = parts.get(name)                 if row is None:                     row = {@@ -97,7 +118,9 @@ def handle_assembly_bom(app: adsk.core.Application, args: dict) -> dict:                 else:                     row["quantity"] += 1             elif has_children:+                sub_stack.append(name)                 walk(children)+                sub_stack.pop()      walk(design.rootComponent.occurrences)     rows = list(parts.values())@@ -109,14 +132,35 @@ def handle_assembly_bom(app: adsk.core.Application, args: dict) -> dict:     except Exception:         pass +    # Inverse advisory (Oliver, PR #38 intent): a DESCENDED subassembly dominated by many+    # identical small parts walks and quacks like a purchased unit that should be one BOM line.+    UNDER_COLLAPSE_MIN = 12+    possible_under = []+    for sub, stats in sub_stats.items():+        part, n = max(stats.items(), key=lambda kv: kv[1])+        if n >= UNDER_COLLAPSE_MIN:+            possible_under.append({+                "subassembly": sub, "dominantPart": part, "instances": n,+                "why": "descended subassembly dominated by %d identical parts; if it is a purchased unit, add a matching substring to treatAsUnit and it becomes one line" % n})+     out = {         "success": True,         "design": doc_name,         "partCount": len(rows),         "totalInstances": sum(r["quantity"] for r in rows),         "treatAsUnit": kit,+        "collapsedUnits": sorted(collapsed.values(), key=lambda c: -c["quantity"]),+        "possibleUnderCollapse": sorted(possible_under, key=lambda a: -a["instances"]),         "parts": rows,     }+    if collapsed or possible_under:+        bits = []+        if collapsed:+            bits.append("%d subassembly type(s) were COLLAPSED to one line each by treatAsUnit name-matching (see collapsedUnits); if any is one of YOUR designs rather than a purchased unit, re-run with an explicit treatAsUnit list (or []) to explode it" % len(collapsed))+        if possible_under:+            bits.append("%d descended subassembly(ies) look like purchased units (see possibleUnderCollapse); add a matching substring to treatAsUnit to collapse them" % len(possible_under))+        out["_hint"] = ". ".join(bits) + "."+      outpath = args.get("outputPath")     if outpath:
adom-bridge-fusion-manifest.json+4−4
@@ -1,10 +1,10 @@ {     "manifest_version": 1,     "name": "fusion360",-    "version": "1.9.2",-    "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.2/adom-bridge-fusion-v1.9.2.zip",-    "sha256": "6c4b942a9856ccd9aa5be4c63c3a144b551582014d17b952e36844b9f3688813",-    "size": 300217,+    "version": "1.9.3",+    "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.3/adom-bridge-fusion-v1.9.3.zip",+    "sha256": "6a1b1ec071e8eb9c347f3cfc3bd386e370f73098cce9a0c7ec7e4df0ecebe93e",+    "size": 301185,     "verbPrefixes": [         "fusion_"     ],
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.9.2",+  "version": "1.9.3",   "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.",   "homepage": "https://wiki.adom.inc/adom/fusion-bridge",   "author": "Adom Inc.",