@windypro-rourou/dsh-eda 0.1.10 → 0.1.11

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.
Files changed (2) hide show
  1. package/lib/index.js +55 -47
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -2004,14 +2004,17 @@ function edaExportTool(backend) {
2004
2004
 
2005
2005
  /**
2006
2006
  * eda_sch_to_pcb:把当前原理图的器件复制到 PCB(替代不可靠的 importChanges)。
2007
- * 读原理图元件 → getState_Component() 取 device → 在 PCB 网格放置(layer 1)。
2007
+ * 实测要点:原理图器件用 getState_Component() 取 {libraryUuid, uuid, name},
2008
+ * 但 pcb_PrimitiveComponent.create 需要完整 ILIB_DeviceItem —— 用 component.name
2009
+ * 再 lib_Device.search(name) 得到可放置的 device(3/3 实测通过)。
2008
2010
  */
2009
2011
  function edaSchToPcbTool(backend) {
2010
2012
  return defineTool({
2011
2013
  name: 'eda_sch_to_pcb',
2012
2014
  description: 'Copy the components of the CURRENT schematic onto the project PCB (direct placement). ' +
2013
- 'This is the reliable replacement for the unreliable PCB_Document.importChanges() (returns true but places nothing). ' +
2014
- 'Reads each schematic component via getState_Component(), opens the project PCB and places it on layer 1 in a grid. ' +
2015
+ 'Reliable replacement for PCB_Document.importChanges() (which returns true but places nothing). ' +
2016
+ 'Reads each schematic component via getState_Component() (libraryUuid/uuid/name), resolves it with lib_Device.search(name), ' +
2017
+ 'opens the project PCB and places it on layer 1 in a grid. ' +
2015
2018
  'Triggers: 原理图转PCB, 复制器件到PCB, sch to pcb, 同步原理图到PCB.',
2016
2019
  parameters: {
2017
2020
  spacing: { type: 'integer', description: '可选:网格间距(mil,默认 900)' },
@@ -2032,6 +2035,7 @@ function edaSchToPcbTool(backend) {
2032
2035
  additionalProperties: false,
2033
2036
  properties: {
2034
2037
  designator: { type: 'string' },
2038
+ name: { type: 'string' },
2035
2039
  ok: { type: 'boolean', required: true },
2036
2040
  uuid: { type: 'string' },
2037
2041
  err: { type: 'string' },
@@ -2045,7 +2049,9 @@ function edaSchToPcbTool(backend) {
2045
2049
  render: (_args, value) => {
2046
2050
  if (value?.ok !== true) return [{ type: 'text', text: '原理图转 PCB 失败: ' + (value?.error ?? '?') }]
2047
2051
  const lines = ['✅ 原理图 → PCB:' + (value.placedOk ?? 0) + '/' + (value.total ?? 0) + ' 个器件已放置到 ' + (value.pcbUuid ?? '')]
2048
- for (const p of value.placed ?? []) if (!p.ok && p.err) lines.push(' ⚠ ' + (p.designator ?? '?') + ': ' + p.err)
2052
+ for (const p of value.placed ?? []) {
2053
+ if (!p.ok) lines.push(' ⚠ ' + (p.designator || p.name || '?') + ': ' + (p.err ?? 'failed'))
2054
+ }
2049
2055
  if (value.note) lines.push(value.note)
2050
2056
  return [{ type: 'text', text: lines.join('\n') }]
2051
2057
  },
@@ -2061,59 +2067,61 @@ function edaSchToPcbTool(backend) {
2061
2067
  const actId = pushPendingActivity({ sid, tool: 'eda_sch_to_pcb', action: '原理图转PCB', code: 'spacing=' + spacing })
2062
2068
  const t0 = Date.now()
2063
2069
  try {
2064
- const pre = await backend.execute('return await (async () => {' +
2065
- 'const comps = await eda.sch_PrimitiveComponent.getAll();' +
2066
- 'const list = [];' +
2067
- 'for (const c of (comps || [])) {' +
2068
- ' let type = ""; try { type = c.getState_ComponentType(); } catch (e) {}' +
2069
- ' if (["sheet","netflag","netport","wire","bus","text"].includes(type)) continue;' +
2070
- ' let device = null; try { device = c.getState_Component ? c.getState_Component() : null; } catch (e) {}' +
2071
- ' if (!device) continue;' +
2072
- ' list.push({ id: (() => { try { return c.getState_PrimitiveId(); } catch (e) { return ""; } })(),' +
2073
- ' designator: (() => { try { return c.getState_Designator(); } catch (e) { return ""; } })() });' +
2074
- '}' +
2075
- 'const proj = await eda.dmt_Project.getCurrentProjectInfo();' +
2076
- 'const pcbUuid = proj?.data?.[0]?.pcb?.uuid;' +
2077
- 'return { ok: true, count: list.length, pcbUuid }; })();', 30000)
2078
- let preData = null
2079
- try { preData = JSON.parse(pre.result ?? 'null') } catch (e) { preData = null }
2080
- if (!pre.ok || !preData || preData.ok !== true) {
2081
- updateActivity(actId, { status: 'error', ok: false, action: '原理图转PCB', error: '读取原理图器件失败', durationMs: Date.now() - t0 })
2082
- return { ok: false, pcbUuid: '', total: 0, placedOk: 0, placed: [], note: '', error: '读取原理图器件失败:' + (pre.error ?? '') }
2083
- }
2084
- if (!preData.pcbUuid) {
2085
- updateActivity(actId, { status: 'error', ok: false, action: '原理图转PCB', error: '当前工程没有 PCB', durationMs: Date.now() - t0 })
2086
- return { ok: false, pcbUuid: '', total: Number(preData.count ?? 0), placedOk: 0, placed: [], note: '', error: '当前工程没有 PCB(先 createPcb 建板)' }
2087
- }
2070
+ // 一次性桥代码(与实测脚本一致):打开原理图 → getAll(重试) → 取 component.name → 打开 PCB → search(name)→create → 保存
2088
2071
  const code = 'return await (async () => {' +
2089
2072
  'const sleep = (ms) => new Promise(r => setTimeout(r, ms));' +
2090
- 'const comps = await eda.sch_PrimitiveComponent.getAll();' +
2091
- 'const list = [];' +
2092
- 'for (const c of (comps || [])) {' +
2073
+ 'const proj = await eda.dmt_Project.getCurrentProjectInfo();' +
2074
+ 'const first = (proj?.data || [])[0] || {};' +
2075
+ 'const schPage = first?.schematic?.page?.[0]?.uuid;' +
2076
+ 'const pcbUuid = first?.pcb?.uuid;' +
2077
+ 'if (!schPage) return { ok: false, err: "当前工程没有原理图页" };' +
2078
+ 'if (!pcbUuid) return { ok: false, err: "当前工程没有 PCB(先 createPcb 建板)" };' +
2079
+ // 打开原理图
2080
+ 'await eda.dmt_EditorControl.openDocument(schPage);' +
2081
+ 'await sleep(2200);' +
2082
+ 'let comps = null;' +
2083
+ 'for (let i = 0; i < 5 && !comps; i++) {' +
2084
+ ' try { const r = await eda.sch_PrimitiveComponent.getAll(); if (Array.isArray(r)) comps = r; } catch (e) {}' +
2085
+ ' if (!comps) await sleep(800);' +
2086
+ '}' +
2087
+ 'if (!comps) return { ok: false, err: "原理图矩形读取失败(getAll 重试 5 次)" };' +
2088
+ 'const parts = [];' +
2089
+ 'for (const c of comps) {' +
2093
2090
  ' let type = ""; try { type = c.getState_ComponentType(); } catch (e) {}' +
2094
- ' if (["sheet","netflag","netport","wire","bus","text"].includes(type)) continue;' +
2095
- ' let device = null; try { device = c.getState_Component ? c.getState_Component() : null; } catch (e) {}' +
2096
- ' if (!device) continue;' +
2097
- ' list.push({ designator: (() => { try { return c.getState_Designator(); } catch (e) { return ""; } })(), device });' +
2091
+ ' if (!["part", "component"].includes(type)) continue;' +
2092
+ ' let comp = null; try { comp = c.getState_Component ? c.getState_Component() : null; } catch (e) {}' +
2093
+ ' if (!comp || !comp.name) continue;' +
2094
+ ' parts.push({' +
2095
+ ' designator: (() => { try { return c.getState_Designator(); } catch (e) { return ""; } })(),' +
2096
+ ' name: String(comp.name),' +
2097
+ ' libraryUuid: comp.libraryUuid ? String(comp.libraryUuid) : "",' +
2098
+ ' uuid: comp.uuid ? String(comp.uuid) : "",' +
2099
+ ' });' +
2098
2100
  '}' +
2099
- 'const proj = await eda.dmt_Project.getCurrentProjectInfo();' +
2100
- 'const pcbUuid = proj?.data?.[0]?.pcb?.uuid;' +
2101
- 'if (!pcbUuid) return { ok: false, err: "no pcb" };' +
2101
+ // 打开 PCB
2102
2102
  'await eda.dmt_EditorControl.openDocument(pcbUuid);' +
2103
- 'await sleep(1800);' +
2103
+ 'await sleep(2000);' +
2104
2104
  'const spacing = SPACING;' +
2105
2105
  'const placed = [];' +
2106
2106
  'let x = 1500, y = 1200, col = 0;' +
2107
- 'for (const it of list) {' +
2107
+ 'for (const p of parts) {' +
2108
+ ' const rec = { designator: p.designator, name: p.name };' +
2108
2109
  ' try {' +
2109
- ' const comp = await eda.pcb_PrimitiveComponent.create(it.device, 1, x, y);' +
2110
- ' placed.push({ designator: String(it.designator || ""), ok: !!comp, uuid: comp && comp.getState_PrimitiveId ? comp.getState_PrimitiveId() : "" });' +
2111
- ' } catch (e) { placed.push({ designator: String(it.designator || ""), ok: false, err: String(e).slice(0, 80) }); }' +
2110
+ ' const items = await eda.lib_Device.search(p.name, undefined, undefined, undefined, 5, 1);' +
2111
+ ' const dev = items && items[0];' +
2112
+ ' if (!dev) { rec.ok = false; rec.err = "search 无结果"; placed.push(rec); continue; }' +
2113
+ ' const c = await eda.pcb_PrimitiveComponent.create(dev, 1, x, y);' +
2114
+ ' rec.ok = !!c;' +
2115
+ ' if (c && c.getState_PrimitiveId) rec.uuid = c.getState_PrimitiveId();' +
2116
+ ' } catch (e) { rec.ok = false; rec.err = String(e && e.message || e).slice(0, 100); }' +
2117
+ ' placed.push(rec);' +
2112
2118
  ' col++; if (col % 6 === 0) { x = 1500; y += spacing; } else x += spacing;' +
2113
2119
  ' await sleep(120);' +
2114
2120
  '}' +
2115
2121
  'try { await eda.pcb_Document.save(); } catch (e) {}' +
2116
- 'return { ok: true, pcbUuid, total: list.length, placedOk: placed.filter(p => p.ok).length, placed }; })();'
2122
+ 'const pc = await eda.pcb_PrimitiveComponent.getAll().catch(() => null);' +
2123
+ 'return { ok: true, pcbUuid, total: parts.length, placedOk: placed.filter(p => p.ok).length, placed, pcbComponentCount: pc ? pc.length : null };' +
2124
+ '})();'
2117
2125
  const r = await backend.execute(code.replace('SPACING', String(spacing)), 120000)
2118
2126
  if (!r.ok) {
2119
2127
  updateActivity(actId, { status: 'error', ok: false, action: '原理图转PCB', error: r.error, durationMs: Date.now() - t0 })
@@ -2124,10 +2132,10 @@ function edaSchToPcbTool(backend) {
2124
2132
  if (!parsed) return { ok: false, pcbUuid: '', total: 0, placedOk: 0, placed: [], note: '', error: '结果解析失败' }
2125
2133
  if (parsed.ok !== true) {
2126
2134
  updateActivity(actId, { status: 'error', ok: false, action: '原理图转PCB', error: String(parsed.err ?? 'failed'), durationMs: Date.now() - t0 })
2127
- return { ok: false, pcbUuid: '', total: Number(parsed.total ?? 0), placedOk: 0, placed: [], note: '', error: String(parsed.err ?? '原理图转 PCB 失败') }
2135
+ return { ok: false, pcbUuid: '', total: 0, placedOk: 0, placed: [], note: '', error: String(parsed.err ?? '原理图转 PCB 失败') }
2128
2136
  }
2129
2137
  const placed = (parsed.placed ?? []).map((p) => {
2130
- const row = { designator: String(p.designator ?? ''), ok: p.ok === true }
2138
+ const row = { designator: String(p.designator ?? ''), name: String(p.name ?? ''), ok: p.ok === true }
2131
2139
  if (p.uuid) row.uuid = String(p.uuid)
2132
2140
  if (p.err) row.err = String(p.err)
2133
2141
  return row
@@ -2139,7 +2147,7 @@ function edaSchToPcbTool(backend) {
2139
2147
  return {
2140
2148
  ok: true, pcbUuid: String(parsed.pcbUuid ?? ''), total: Number(parsed.total ?? 0),
2141
2149
  placedOk: Number(parsed.placedOk ?? 0), placed,
2142
- note: '放置坐标是元件原点(封装可能偏移);位号沿用原理图但 PCB 上可能为空,需人工确认。',
2150
+ note: '已按 component.name 重新检索器件放置(比 translate device 引用更可靠);位号沿用原理图,坐标=元件原点(封装可能偏移),布线与网络仍需后续处理。',
2143
2151
  }
2144
2152
  } catch (err) {
2145
2153
  const msg = err instanceof Error ? err.message : String(err)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windypro-rourou/dsh-eda",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "dsh-lichuang-eda (JCL-EDA): 嘉立创 EDA 助手 DSH 插件 —— 官方 eda.* API 云端实时生成(原理图/PCB)+ 离线标准版 JSON 兜底 + 面板记录式时间线(撤回/清空/紧急保存)。开箱即用:npm i 后 cordis.patch.yml insert 一行即挂载。",
6
6
  "packageManager": "pnpm@11.22.0",