dsh-plc-decode 0.1.4 → 0.2.0

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.
package/index.js CHANGED
@@ -24,9 +24,13 @@ import { basename, dirname, extname, isAbsolute, join, resolve } from 'node:path
24
24
  import { fileURLToPath } from 'node:url'
25
25
  import z from '@deepseek-ai/schemastery'
26
26
  import { writeSmartPack, writeSourcePack } from './assets/decode-pack.mjs'
27
+ import { registerHmi } from './hmi.js'
27
28
 
28
29
  export const name = 'dsh-plc-decode'
29
- export const inject = ['tools', 'commands', 'shell', 'sandboxPolicy']
30
+ export const inject = ['skills', 'tools', 'commands', 'shell', 'sandboxPolicy']
31
+
32
+ /** 报错尾行:插件出问题时,使用者第一眼看的就是错误文本。 */
33
+ const FEEDBACK_HINT = '遇到问题:加 QQ 号 2171960697,或开 Issue https://github.com/taoziliyouchong/dsh-plc-decode/issues'
30
34
 
31
35
  /** 插件配置(DSH 官方 Config 机制:cordis.patch.yml 里本插件行的 config 传进 apply)。
32
36
  * 2026-11-05 加:让装插件的人不改代码就能换解码产物的存放位置。 */
@@ -2609,6 +2613,9 @@ export function apply(ctx, config = {}) {
2609
2613
  const cfg = config && typeof config === 'object' ? config : {}
2610
2614
  if (typeof cfg.archiveDir === 'string' && cfg.archiveDir.trim()) ARCHIVE_ROOT = cfg.archiveDir.trim()
2611
2615
 
2616
+ // 触摸屏(HMI)工程解码:画面/控件/变量/地址,与 PLC 侧共用同一插件行(hmi.js)。
2617
+ registerHmi(ctx)
2618
+
2612
2619
  // ── 工具一:统一入口(四格式自动识别分发)──
2613
2620
  ctx.tools.register({
2614
2621
  name: 'plc-decode',
@@ -3210,7 +3217,7 @@ export function apply(ctx, config = {}) {
3210
3217
  if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) throw new Error('第二个参数应是地址,或给出"起hex 止hex"')
3211
3218
  return { kind: 'success', text: `梯形图元素序列(${basename(abs)}):\n${ladderElementsRange(data, from, to)}` }
3212
3219
  } catch (error) {
3213
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
3220
+ return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
3214
3221
  }
3215
3222
  },
3216
3223
  })
@@ -3259,7 +3266,7 @@ export function apply(ctx, config = {}) {
3259
3266
  const result = kind === 'smart' ? decodeSmartFile(abs, outDir, sessionBase(agent)) : decodeSmartLib(abs, outDir, sessionBase(agent))
3260
3267
  return ({ kind: 'success', text: `已解码完成(类型: ${kind}):\n${result.proceed}` })
3261
3268
  } catch (error) {
3262
- return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
3269
+ return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
3263
3270
  }
3264
3271
  },
3265
3272
  })
@@ -3277,7 +3284,7 @@ export function apply(ctx, config = {}) {
3277
3284
  : `源码在 ${outd}\\pous\\ 下(看程序读它),索引在 ${outd}\\00-索引.md`
3278
3285
  return { kind: 'success', text: `已解码完成,输出目录:\n${outd}\n\n${text}\n\n${tail}` }
3279
3286
  } catch (error) {
3280
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
3287
+ return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
3281
3288
  }
3282
3289
  },
3283
3290
  })
@@ -3292,7 +3299,7 @@ export function apply(ctx, config = {}) {
3292
3299
  const { text } = await decodeDvpText(ctx, dvp, signal)
3293
3300
  return { kind: 'success', text }
3294
3301
  } catch (error) {
3295
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
3302
+ return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
3296
3303
  }
3297
3304
  },
3298
3305
  })
@@ -3314,7 +3321,7 @@ export function apply(ctx, config = {}) {
3314
3321
  if (!check.ok) throw new Error(check.text)
3315
3322
  return { kind: 'success', text: `校验完成(源码目录 ${outd}):\n\n${check.text}` }
3316
3323
  } catch (error) {
3317
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
3324
+ return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
3318
3325
  }
3319
3326
  },
3320
3327
  })
@@ -3336,7 +3343,7 @@ export function apply(ctx, config = {}) {
3336
3343
  const need = Number(parts[1] ?? 0) || 0
3337
3344
  return ({ kind: 'success', text: conflictReport(o.userSyms, o.records, ptrRefs, need, 4096) })
3338
3345
  } catch (error) {
3339
- return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
3346
+ return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
3340
3347
  }
3341
3348
  },
3342
3349
  })
@@ -3357,7 +3364,7 @@ export function apply(ctx, config = {}) {
3357
3364
  const result = raw[0] === 0x44 ? decodeSmartLib(abs, outDir, sessionBase(agent)) : decodeSmartFile(abs, outDir, sessionBase(agent))
3358
3365
  return ({ kind: 'success', text: result.proceed })
3359
3366
  } catch (error) {
3360
- return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
3367
+ return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
3361
3368
  }
3362
3369
  },
3363
3370
  })
@@ -3373,7 +3380,7 @@ export function apply(ctx, config = {}) {
3373
3380
  const { notes, report } = await runDiff(ctx, oldP, newP, signal, sessionBase(agent))
3374
3381
  return { kind: 'success', text: notes.length ? `${notes.join('\n')}\n\n${report}` : report }
3375
3382
  } catch (error) {
3376
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
3383
+ return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
3377
3384
  }
3378
3385
  },
3379
3386
  })
@@ -3393,7 +3400,7 @@ export function apply(ctx, config = {}) {
3393
3400
  const { proceed, report } = smartDiff(resolve(parts[0]), resolve(parts[1]), outDir, sessionBase(agent))
3394
3401
  return { kind: 'success', text: `${proceed}\n\n${report}` }
3395
3402
  } catch (error) {
3396
- return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
3403
+ return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
3397
3404
  }
3398
3405
  },
3399
3406
  })
@@ -3421,7 +3428,7 @@ export function apply(ctx, config = {}) {
3421
3428
  }
3422
3429
  return ({ kind: 'success', text: `地址占用与空闲段分析(${isDir ? '已解码目录' : basename(abs)}):\n${freeSpaceReport(userSyms, records)}` })
3423
3430
  } catch (error) {
3424
- return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
3431
+ return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
3425
3432
  }
3426
3433
  },
3427
3434
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-plc-decode",
3
- "version": "0.1.4",
4
- "description": "在 DSH 里读 PLC 工程文件:CoDeSys .project、台达 .dvp、西门子 S7-200 SMART .smart/.smartlib 统一解码成可读报告与结构化数据,另带新旧工程对比、地址体检与连线还原。",
3
+ "version": "0.2.0",
4
+ "description": "在 DSH 里读工控工程文件:PLC(CoDeSys、台达、西门子 SMART)与触摸屏(西门子 WinCC flexible SMART、繁易 FStudio、欧姆龙 NB、昆仑通态 MCGS)里的程序、画面、控件、变量与地址,读成能查的报告与表。",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "index.js",
14
+ "hmi.js",
14
15
  "invariant.js",
15
16
  "cordis.patch.yml",
16
17
  "assets",
@@ -21,7 +22,7 @@
21
22
  ],
22
23
  "engines": {
23
24
  "node": "^22.19.0 || >=24.0.0",
24
- "dsh": ">=0.2.1-rc.5 <0.3.0-0"
25
+ "dsh": ">=0.2.1-0 <0.3.0-0"
25
26
  },
26
27
  "dsh": {
27
28
  "bundle": {
@@ -38,6 +39,13 @@
38
39
  "codesys",
39
40
  "delta-dvp",
40
41
  "ladder",
42
+ "hmi",
43
+ "touchscreen",
44
+ "wincc-flexible-smart",
45
+ "fstudio",
46
+ "omron",
47
+ "nb-designer",
48
+ "mcgs",
41
49
  "industrial-automation",
42
50
  "leadshine",
43
51
  "leadsysstudio",
@@ -45,8 +53,9 @@
45
53
  ],
46
54
  "scripts": {
47
55
  "smoke": "node scripts/smoke.mjs",
48
- "check": "node --check index.js && node --check invariant.js && node --check assets/decode-pack.mjs && node --check assets/smart-lad.mjs && node --check assets/smart-net-trace.mjs"
56
+ "check": "node --check index.js && node --check hmi.js && node --check invariant.js && node --check assets/decode-pack.mjs && node --check assets/hmismart-decode.mjs && node --check assets/hmismart-diff.mjs && node --check assets/hmismart-report.mjs && node --check assets/nb-decode.mjs && node --check assets/mcgs-decode.mjs && node --check assets/smart-lad.mjs && node --check assets/smart-net-trace.mjs"
49
57
  },
58
+ "author": "taoziliyouchong",
50
59
  "license": "MIT",
51
60
  "repository": {
52
61
  "type": "git",
package/scripts/smoke.mjs CHANGED
@@ -15,8 +15,8 @@ import { join } from 'node:path'
15
15
 
16
16
  import { apply, inject, name as pluginName } from '../index.js'
17
17
 
18
- const EXPECTED_TOOLS = 12
19
- const EXPECTED_COMMANDS = 12
18
+ const EXPECTED_TOOLS = 18
19
+ const EXPECTED_COMMANDS = 15
20
20
 
21
21
  let failed = 0
22
22
  const pass = (m) => console.log(` ok ${m}`)
@@ -60,9 +60,11 @@ function createShell() {
60
60
 
61
61
  const tools = new Map()
62
62
  const commands = new Map()
63
+ const skillProviders = []
63
64
  const ctx = {
64
65
  tools: { register: (t) => tools.set(t.name, t) },
65
66
  commands: { register: (c) => commands.set(c.name, c) },
67
+ skills: { registerProvider: (factory) => skillProviders.push(factory) },
66
68
  shell: createShell(),
67
69
  sandboxPolicy: { resolve: () => undefined },
68
70
  }
@@ -100,6 +102,9 @@ function checkRegistrations() {
100
102
  if (!cmd.description) fail(`命令 /${cmdName} 缺描述`)
101
103
  }
102
104
  pass('各命令 handler / description')
105
+
106
+ if (skillProviders.length === 0) fail('未注册技能 provider')
107
+ else pass(`技能 provider ${skillProviders.length} 个`)
103
108
  }
104
109
 
105
110
  async function checkBadFile() {