dsh-plc-decode 0.1.5 → 0.2.1
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 +43 -11
- 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-i18n-workflow.md +165 -0
- package/assets/fstudio-tables.py +77 -0
- package/assets/fstudio_i18n.py +608 -0
- package/assets/hmi-decode-workflow.md +53 -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/nb-textlib-workflow.md +93 -0
- package/assets/wincc-text-i18n-workflow.md +96 -0
- package/assets/wincc_i18n.py +504 -0
- package/cordis.patch.yml +3 -1
- package/hmi.js +580 -0
- package/index.js +19 -16
- 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
|
})
|
|
@@ -3227,11 +3234,7 @@ export function apply(ctx, config = {}) {
|
|
|
3227
3234
|
`解析方式: ${isAbsolute(ARCHIVE_ROOT) ? '绝对路径,原样使用' : `相对路径,按会话工作区解析(本进程 cwd = ${process.cwd()})`}`,
|
|
3228
3235
|
'默认落点: <归档根>\\<工程文件所在文件夹名>\\(同一文件夹里有多个工程文件时用 <文件夹名>_<文件名>)',
|
|
3229
3236
|
'',
|
|
3230
|
-
'改配置(改完重启 dsh 生效):在 DSH profile 的 cordis.patch.yml 里给本插件那行加 config
|
|
3231
|
-
' - id: dsh-plc-decode',
|
|
3232
|
-
' name: dsh-plc-decode',
|
|
3233
|
-
' config:',
|
|
3234
|
-
' archiveDir: D:/plc/解码归档',
|
|
3237
|
+
'改配置(改完重启 dsh 生效):在 DSH profile 的 cordis.patch.yml 里给本插件那行加 config,只写 archiveDir(归档根目录)。',
|
|
3235
3238
|
'',
|
|
3236
3239
|
'只改这一次、不写配置:解码时传 outDir 参数(工具参数或 /plc-decode 的第二个参数)。',
|
|
3237
3240
|
].join('\n'),
|
|
@@ -3259,7 +3262,7 @@ export function apply(ctx, config = {}) {
|
|
|
3259
3262
|
const result = kind === 'smart' ? decodeSmartFile(abs, outDir, sessionBase(agent)) : decodeSmartLib(abs, outDir, sessionBase(agent))
|
|
3260
3263
|
return ({ kind: 'success', text: `已解码完成(类型: ${kind}):\n${result.proceed}` })
|
|
3261
3264
|
} catch (error) {
|
|
3262
|
-
return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
|
|
3265
|
+
return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
|
|
3263
3266
|
}
|
|
3264
3267
|
},
|
|
3265
3268
|
})
|
|
@@ -3277,7 +3280,7 @@ export function apply(ctx, config = {}) {
|
|
|
3277
3280
|
: `源码在 ${outd}\\pous\\ 下(看程序读它),索引在 ${outd}\\00-索引.md`
|
|
3278
3281
|
return { kind: 'success', text: `已解码完成,输出目录:\n${outd}\n\n${text}\n\n${tail}` }
|
|
3279
3282
|
} catch (error) {
|
|
3280
|
-
return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
|
|
3283
|
+
return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
|
|
3281
3284
|
}
|
|
3282
3285
|
},
|
|
3283
3286
|
})
|
|
@@ -3292,7 +3295,7 @@ export function apply(ctx, config = {}) {
|
|
|
3292
3295
|
const { text } = await decodeDvpText(ctx, dvp, signal)
|
|
3293
3296
|
return { kind: 'success', text }
|
|
3294
3297
|
} catch (error) {
|
|
3295
|
-
return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
|
|
3298
|
+
return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
|
|
3296
3299
|
}
|
|
3297
3300
|
},
|
|
3298
3301
|
})
|
|
@@ -3314,7 +3317,7 @@ export function apply(ctx, config = {}) {
|
|
|
3314
3317
|
if (!check.ok) throw new Error(check.text)
|
|
3315
3318
|
return { kind: 'success', text: `校验完成(源码目录 ${outd}):\n\n${check.text}` }
|
|
3316
3319
|
} catch (error) {
|
|
3317
|
-
return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
|
|
3320
|
+
return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
|
|
3318
3321
|
}
|
|
3319
3322
|
},
|
|
3320
3323
|
})
|
|
@@ -3336,7 +3339,7 @@ export function apply(ctx, config = {}) {
|
|
|
3336
3339
|
const need = Number(parts[1] ?? 0) || 0
|
|
3337
3340
|
return ({ kind: 'success', text: conflictReport(o.userSyms, o.records, ptrRefs, need, 4096) })
|
|
3338
3341
|
} catch (error) {
|
|
3339
|
-
return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
|
|
3342
|
+
return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
|
|
3340
3343
|
}
|
|
3341
3344
|
},
|
|
3342
3345
|
})
|
|
@@ -3357,7 +3360,7 @@ export function apply(ctx, config = {}) {
|
|
|
3357
3360
|
const result = raw[0] === 0x44 ? decodeSmartLib(abs, outDir, sessionBase(agent)) : decodeSmartFile(abs, outDir, sessionBase(agent))
|
|
3358
3361
|
return ({ kind: 'success', text: result.proceed })
|
|
3359
3362
|
} catch (error) {
|
|
3360
|
-
return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
|
|
3363
|
+
return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
|
|
3361
3364
|
}
|
|
3362
3365
|
},
|
|
3363
3366
|
})
|
|
@@ -3373,7 +3376,7 @@ export function apply(ctx, config = {}) {
|
|
|
3373
3376
|
const { notes, report } = await runDiff(ctx, oldP, newP, signal, sessionBase(agent))
|
|
3374
3377
|
return { kind: 'success', text: notes.length ? `${notes.join('\n')}\n\n${report}` : report }
|
|
3375
3378
|
} catch (error) {
|
|
3376
|
-
return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
|
|
3379
|
+
return { kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT }
|
|
3377
3380
|
}
|
|
3378
3381
|
},
|
|
3379
3382
|
})
|
|
@@ -3393,7 +3396,7 @@ export function apply(ctx, config = {}) {
|
|
|
3393
3396
|
const { proceed, report } = smartDiff(resolve(parts[0]), resolve(parts[1]), outDir, sessionBase(agent))
|
|
3394
3397
|
return { kind: 'success', text: `${proceed}\n\n${report}` }
|
|
3395
3398
|
} catch (error) {
|
|
3396
|
-
return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
|
|
3399
|
+
return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
|
|
3397
3400
|
}
|
|
3398
3401
|
},
|
|
3399
3402
|
})
|
|
@@ -3421,7 +3424,7 @@ export function apply(ctx, config = {}) {
|
|
|
3421
3424
|
}
|
|
3422
3425
|
return ({ kind: 'success', text: `地址占用与空闲段分析(${isDir ? '已解码目录' : basename(abs)}):\n${freeSpaceReport(userSyms, records)}` })
|
|
3423
3426
|
} catch (error) {
|
|
3424
|
-
return ({ kind: 'error', text: error instanceof Error ? error.message : String(error) })
|
|
3427
|
+
return ({ kind: 'error', text: (error instanceof Error ? error.message : String(error)) + '\n\n' + FEEDBACK_HINT })
|
|
3425
3428
|
}
|
|
3426
3429
|
},
|
|
3427
3430
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plc-decode",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "在 DSH
|
|
3
|
+
"version": "0.2.1",
|
|
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-0
|
|
25
|
+
"dsh": ">=0.2.1-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() {
|