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/README.en.md +11 -2
- package/README.md +42 -4
- package/assets/FStudio/346/226/207/346/234/254/345/272/223/345/257/274/345/205/245/346/240/274/345/274/217.md +45 -0
- package/assets/fstudio-tables.py +77 -0
- package/assets/fstudio_i18n.py +608 -0
- package/assets/hmismart-decode.mjs +452 -0
- package/assets/hmismart-diff.mjs +206 -0
- package/assets/hmismart-report.mjs +192 -0
- package/assets/mcgs-decode.mjs +140 -0
- package/assets/nb-decode.mjs +107 -0
- package/assets/wincc_i18n.py +504 -0
- package/assets/zzuser-fstudio-i18n-workflow.md +165 -0
- package/assets/zzuser-hmi-decode-workflow.md +53 -0
- package/assets/zzuser-nb-textlib-workflow.md +93 -0
- package/assets/zzuser-wincc-text-i18n-workflow.md +96 -0
- package/cordis.patch.yml +3 -1
- package/hmi.js +580 -0
- package/index.js +18 -11
- package/package.json +13 -4
- package/scripts/smoke.mjs +7 -2
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.
|
|
4
|
-
"description": "在 DSH
|
|
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-
|
|
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 =
|
|
19
|
-
const EXPECTED_COMMANDS =
|
|
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() {
|