dsh-plc-decode 0.1.5 → 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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # dsh-plc-decode
2
2
 
3
+ [中文](README.md) | **English**
4
+
3
5
  Read PLC project files directly inside [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). The plugin detects the format from the file header, decodes the project into readable reports and structured data, and provides project diffing, address audits, element-level read/write analysis and ladder-logic reconstruction.
4
6
 
5
7
  **Read-only**: project files are parsed, never modified.
@@ -65,7 +67,7 @@ The smart decoder writes a source fingerprint (size + mtime) into the report and
65
67
 
66
68
  ## Limitations
67
69
 
68
- - HMI projects (`.hmismart`, `.fsprj`) are out of scope.
70
+ - HMI projects (`.hmismart`, `.fsprj`, `.nbp`, `.mcp`) are decoded by the same plugin row: screens / controls / tags / addresses, tables written to `解码归档/<project>/`. Control-to-tag references in `.hmismart` are partial (only same-segment and UUID hits); the rest is reported as "未解出".
69
71
  - Pin-level arguments of library call boxes (Slave / RW / Addr / Count / DataPtr) cannot be decoded; the tools say so instead of guessing.
70
72
  - Encrypted or specially compressed projects may fail to decode; the decoder reports an error rather than a half-true result.
71
73
  - Ladder series/parallel topology is reconstructed from instruction records (validated network-by-network against official STL exports); library call regions without standard box headers are not covered.
@@ -80,6 +82,13 @@ npm run smoke -- <project> # decode one real project end-to-end
80
82
  npm pack --dry-run # inspect package contents
81
83
  ```
82
84
 
85
+ ## Feedback
86
+
87
+ - Open an issue: https://github.com/taoziliyouchong/dsh-plc-decode/issues — searchable, and others can help.
88
+ - Or reach me on QQ: **2171960697** (mention which plugin you installed when adding the contact).
89
+
90
+ Please include: DSH version, plugin version, the exact error text, and a screenshot.
91
+
83
92
  ## License
84
93
 
85
94
  MIT
package/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  <img src="assets/icon-180.png" width="96" align="right" alt="dsh-plc-decode">
4
4
 
5
- 在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)里直接读 PLC 工程文件:按文件头自动识别格式,把工程解成可读报告与结构化数据,再基于解码结果做新旧对比、地址体检、元素级读/写判定与连线还原。
5
+ **中文** | [English](README.en.md)
6
+
7
+ 在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)里直接读 PLC 工程文件:按文件头自动识别格式,把工程解成可读报告与结构化数据,再基于解码结果做新旧对比、地址体检、元素级读/写判定与连线还原。同一插件行里也承接触摸屏(HMI)工程:画面、控件、变量与地址。
6
8
 
7
9
  **只读**:解析工程文件,不改动原文件。
8
10
 
@@ -17,6 +19,15 @@
17
19
 
18
20
  > 雷赛(Leadshine)用 LeadSysStudio 保存的 `.project` 与 CoDeSys 同格式,走同一条解码链:ST 源码导出到 `pous/<POU>.txt`,可用 `/codesys-check` 做等价编译校验(结构体成员、数组下标、调用存在性、地址唯一、未声明符号)。
19
21
 
22
+ ### 触摸屏(HMI)工程
23
+
24
+ | 文件 | 品牌 | 解出 |
25
+ |---|---|---|
26
+ | `.hmismart` | 西门子 WinCC flexible SMART(Smart Line 屏) | 画面 / 元件 / 文本 / 设备表;旁系 `{{GUID}}.data` 里解变量名、控件名、绑定候选、类型表 |
27
+ | `.fsprj` 或含 `HMI\Frame` 的目录 | 繁易 FStudio / Flexem | 画面 / 控件 / 控件地址(读 `HMI\System\fs2.s3db` 与 `Bin\fshmi.bin`) |
28
+ | `.nbp` | 欧姆龙 NB-Designer | 画面 / 控件 / 地址(XML 直读) |
29
+ | `.mcp` / `.mcgs` | 昆仑通态 MCGS / LSTP Studio | 画面 / 变量 / 报警 / 策略(Access 库) |
30
+
20
31
  ## 安装
21
32
 
22
33
  前提:
@@ -52,6 +63,12 @@ dsh plugin --profile web add github:taoziliyouchong/dsh-plc-decode
52
63
  | `smart-conflict` | 地址冲突体检:重叠冲突(真冲突 / 疑似 / 仅登记)+ 连续空间被切碎 |
53
64
  | `smart-diff-project` | SMART 新旧工程对比:块清单、主程序调用链、I-O 引用、符号表差异 |
54
65
  | `codesys-diff-project` | CoDeSys 新旧工程对比:POU 清单、真实逻辑变化区段、空白差异、字符串表、对象表 |
66
+ | `hmi-decode` | 触摸屏统一入口:按文件识别 `.hmismart` / `.fsprj` / `.nbp` / `.mcp` 并解码 |
67
+ | `hmi-diff-project` | 新旧 `.hmismart` 对比:起始画面、画面表、画面名、配套文件 |
68
+ | `fstudio-scan-texts` | 繁易工程元件文本扫描(按语种槽) |
69
+ | `fstudio-i18n` | 繁易多语言对照表与文本库导入表 |
70
+ | `fstudio-retag` | 已导出文本库表换语种列 |
71
+ | `wincc-text-i18n` | WinCC 项目文本三件套(Screens / Others / Messages)翻译与回写 |
55
72
 
56
73
  ### 斜杠命令(人直接执行)
57
74
 
@@ -69,6 +86,9 @@ dsh plugin --profile web add github:taoziliyouchong/dsh-plc-decode
69
86
  | `/smart-conflict <.smart>` | 地址冲突体检 |
70
87
  | `/smart-ladder <.smart> [地址]` | 元素级读/写判定 |
71
88
  | `/smart-lad <.smart> [块名] [网络号]` | 连线还原(LAD 指令序列) |
89
+ | `/hmi-decode <.hmismart / .fsprj / .nbp / .mcp> [输出目录]` | 触摸屏工程解码 |
90
+ | `/hmi-diff <旧 .hmismart> <新 .hmismart>` | 触摸屏新旧对比 |
91
+ | `/fstudio-retag <TextLib 表> <词典> [输出]` | 文本库表换语种列 |
72
92
 
73
93
  ## 输出位置
74
94
 
@@ -79,6 +99,9 @@ dsh plugin --profile web add github:taoziliyouchong/dsh-plc-decode
79
99
  | `.project` | `00-索引.md`(先读这份)、`pous/<POU>.txt`(ST 源码)、`工程内容报告.md`、`全部字符串.txt`、`对象表.md`、`项目树.md` |
80
100
  | `.dvp` | `00-索引.md`、`解码报告.md`(结构 / 段落注释 / 设备注释 / 指令表)、可选 `程序解读.md` |
81
101
  | `.smart` / `.smartlib` | `00-目录.md`(先读这份)、`解码报告.md`、`程序.lad`、`元素记录.tsv`、`符号地址.tsv`、`分析.md`、`记录.json`、`字符串全录.txt` |
102
+ | `.hmismart` | `10-画面.tsv`、`11-元件.tsv`、`12-文本.tsv`、`13-设备.tsv`、`20-变量.data.tsv`、`21-控件.data.tsv`、`23-控件绑定.data.tsv`、`24-类型表.tsv` |
103
+ | `.fsprj` | `画面.tsv`、`控件.tsv`、`控件地址.tsv`、`变量.tsv` |
104
+ | `.nbp` / `.mcp` | `解码报告.md` + 画面/控件(或变量/报警/策略)JSON |
82
105
 
83
106
  ### 改归档地址(不用改代码)
84
107
 
@@ -97,7 +120,7 @@ dsh plugin --profile web add github:taoziliyouchong/dsh-plc-decode
97
120
 
98
121
  ## 已知边界
99
122
 
100
- - 不处理触摸屏(HMI)工程:`.hmismart`、`.fsprj` 不在本插件范围。
123
+ - 触摸屏工程:`.hmismart` 里只有模板骨架,真实画面与控件在旁系 `{{GUID}}.data`(ES2RT 序列化流),变量名与控件名能解,控件到变量的引用只解出同段共现与 UUID 命中那部分,其余标「未解出」;繁易工程若没建变量,控件地址只在 `Bin\fshmi.bin` 的动作表里;昆仑通态工程需本机有 `.mcp` 样本才能验证。
101
124
  - 库块调用区(如 MBUS_MSG 的 Slave / RW / Addr / Count / DataPtr)的引脚级实参解不出,工具会明确说明,不猜。
102
125
  - 加密或特殊压缩的工程可能解不出;解码器会报错而不是给出半真结果。
103
126
  - `.smart` 的串/并联拓扑由指令记录还原,已用官方导出的 STL 逐网络回归验证;库调用区(无标准盒头)不在覆盖范围内。
@@ -107,7 +130,7 @@ dsh plugin --profile web add github:taoziliyouchong/dsh-plc-decode
107
130
 
108
131
  ```sh
109
132
  npm run check # 语法检查
110
- npm run smoke # 装载插件 + 核对注册面(工具 12 个 / 命令 12 个)
133
+ npm run smoke # 装载插件 + 核对注册面(工具 18 个 / 命令 15 个)
111
134
  npm run smoke -- <工程文件> # 额外对一个真实工程跑端到端解码,核对产物落盘
112
135
  npm pack --dry-run # 看包里内容(node_modules、publish/ 不应进包)
113
136
  ```
@@ -116,14 +139,29 @@ npm pack --dry-run # 看包里内容(node_modules、publish/ 不应
116
139
 
117
140
  ```
118
141
  index.js 插件入口:工具与命令注册、四格式分发、SMART 解码器
142
+ hmi.js 触摸屏解码模块(工具/命令/技能注册,由 index.js 调用)
119
143
  invariant.js 伴随插件:登记包名所有权
120
144
  assets/decode-pack.mjs 解码产物落盘(索引/目录/表格/记录)
121
145
  assets/smart-lad.mjs 连线还原脚本(由 smart-lad 工具调用)
122
146
  assets/smart-net-trace.mjs 网络轨迹脚本(由 smart-net-trace 工具调用)
123
147
  assets/codesys-tools/ CoDeSys / DVP 解码 Python 脚本(只用标准库)
148
+ assets/hmismart-decode.mjs 西门子 .hmismart 解码器(结构表 + .data 名字/绑定/类型表)
149
+ assets/hmismart-diff.mjs .hmismart 新旧对比
150
+ assets/fstudio-tables.py 繁易画面 / 控件 / 控件地址表
151
+ assets/nb-decode.mjs 欧姆龙 NB-Designer 解码器
152
+ assets/mcgs-decode.mjs 昆仑通态 MCGS / LSTP 解码器
124
153
  scripts/smoke.mjs 冒烟测试
125
154
  ```
126
155
 
156
+ ## 反馈
157
+
158
+ 插件有问题,按这个顺序:
159
+
160
+ 1. 开 Issue:https://github.com/taoziliyouchong/dsh-plc-decode/issues —— 能留痕、能搜历史,别人也可能已经遇过;
161
+ 2. 急着问或不想开 Issue,QQ 号:**2171960697**(加好友请注明装的是哪个插件)。
162
+
163
+ 反馈时带上:DSH 版本、插件版本、报错原文、截图。少一样多半要再问一轮。
164
+
127
165
  ## 许可
128
166
 
129
167
  MIT
@@ -0,0 +1,45 @@
1
+ # FStudio 文本库导入格式(繁易 Flexem)
2
+
3
+ 做多语言时,给 FStudio 文本库**导入**用的表,格式必须与软件**导出**的 TextLib 表逐字符一致,
4
+ 否则软件报"文件格式不正确"。踩过一次坑,记在这里。
5
+
6
+ ## 表结构
7
+
8
+ 单表,表名随意(软件导出叫 `TextLib`)。表头**必须**是这 5 列(有俄语就 6 列):
9
+
10
+ | 列 | 内容 | 说明 |
11
+ |---|---|---|
12
+ | 1 | `Guid` | 条目唯一标识,UUID 形如 `20c4864f-29ba-46c4-b9a5-beef83fbcad2`。新条目自己生成 UUID4 |
13
+ | 2 | `Name` | 条目名,建议 ≤50 字,勿换行(软件里列表显示用) |
14
+ | 3 | `Status` | 状态号,**数字**:默认值节点写 `0`,多状态依次 1、2… |
15
+ | 4 | `1-Chinese (Simplified, PRC)-2052` | 简体中文(LCID 2052) |
16
+ | 5 | `2-English (United States)-1033` | 英文(LCID 1033) |
17
+ | 6 | `3-Russian (Russia)-1049` | 俄语(LCID 1049),不用就别加这列 |
18
+
19
+ - 列名里空格、括号、连字符都不能改;`Guid`/`Name`/`Status` 首字母大写。
20
+ - 简体中文那列的列名尤其容易写错成"中文"——**写"中文"就导不进去**。
21
+ - 一个条目若有多状态,导出的表是"Guid 只在首行、后续行 Guid 留空",同 `Name`、`Status` 递增。
22
+ 自己造导入表时**每行都给 Guid** 更稳。
23
+
24
+ ## 生成脚本
25
+
26
+ `出文本库导入表.py`:把中文英文对照表转成上面的格式。
27
+
28
+ ```
29
+ python 出文本库导入表.py <对照表.xlsx> [输出.xlsx]
30
+ ```
31
+
32
+ - 输入表要求:有一列列名含「中文」、一列含「英文」或 `English`。
33
+ - 输出:**一份**可导入的 `.xlsx`(默认只出一份,别一次给多个表)。需要 `.xls` 时加参数:`--xls`。
34
+ - 每个条目 Guid 自动生成、Status=0、Name 取中文前 50 字、英文为空的行标黄底提示。
35
+
36
+ ## 导入前自查(3 条)
37
+
38
+ 1. 表头逐字符对:`Guid` / `Name` / `Status` / `1-Chinese (Simplified, PRC)-2052` / `2-English (United States)-1033`。
39
+ 2. Guid 无重复、非空;Status 是数字不是文本。
40
+ 3. 库里已有的条目先核一遍,别把同名条目导成两条。
41
+
42
+ ## 相关
43
+
44
+ - 文本库在工程里的位置:`Global\TextLibConfigInfo.cfg`;语言表:`HMI\System\LanguageConfigInfo.cfg`。
45
+ - 画面元件想随语种变,还得逐个元件勾「使用文本库」指到条目(FStudio 使用手册 4.13.8 标签编辑器,p.570-571)。
@@ -0,0 +1,77 @@
1
+ # -*- coding: utf-8 -*-
2
+ """fstudio-tables.py —— FStudio(.fsprj) 工程出结构化表(画面/控件)
3
+ 用法: python fstudio-tables.py <工程目录或 .fsprj> [输出目录]
4
+ 工程目录须含 HMI/System/fs2.s3db;输出 画面.tsv / 控件.tsv / 变量.tsv。
5
+ """
6
+ import os
7
+ import sqlite3
8
+ import sys
9
+
10
+ def main():
11
+ if len(sys.argv) < 2:
12
+ print("用法: python fstudio-tables.py <工程目录或 .fsprj> [输出目录]")
13
+ return 2
14
+ src = os.path.abspath(sys.argv[1])
15
+ root = os.path.dirname(src) if src.lower().endswith('.fsprj') else src
16
+ db = os.path.join(root, 'HMI', 'System', 'fs2.s3db')
17
+ if not os.path.exists(db):
18
+ print(f"找不到 {db}(FStudio 工程结构不符)")
19
+ return 3
20
+ out = os.path.abspath(sys.argv[2]) if len(sys.argv) > 2 else os.path.join(root, '解码归档', os.path.basename(root))
21
+ os.makedirs(out, exist_ok=True)
22
+
23
+ c = sqlite3.connect(db)
24
+ def rows(sql):
25
+ try:
26
+ return c.execute(sql).fetchall()
27
+ except Exception:
28
+ return []
29
+
30
+ windows = rows("select WindowId, Name, FullName, WindowType, OrderId from WindowsInfo order by OrderId")
31
+ comps = rows("select ComponentId, Name, GraphicType, WindowId from OperationRecordComponent order by WindowId, Id")
32
+ tags = rows("select * from VariableTagDetailsV2 limit 0") # 只探存在性
33
+
34
+ win_name = {w[0]: (w[2] or w[1]) for w in windows}
35
+ with open(os.path.join(out, '画面.tsv'), 'w', encoding='utf-8') as f:
36
+ f.write('画面ID\t名称\t全名\t类型\t排序\n')
37
+ for w in windows:
38
+ f.write('\t'.join(str(x if x is not None else '') for x in w) + '\n')
39
+ with open(os.path.join(out, '控件.tsv'), 'w', encoding='utf-8') as f:
40
+ f.write('画面\t控件名\t类型\t控件ID\n')
41
+ for comp in comps:
42
+ f.write('\t'.join([win_name.get(comp[3], comp[3] or ''), comp[1] or '', comp[2] or '', comp[0] or '']) + '\n')
43
+ tag_cols = [r[1] for r in c.execute('pragma table_info("VariableTagDetailsV2")')]
44
+ with open(os.path.join(out, '变量.tsv'), 'w', encoding='utf-8') as f:
45
+ f.write('变量名\t类型\t地址\t未解出说明\n')
46
+ if not tag_cols:
47
+ f.write('\t\t\t本工程无 VariableTagDetailsV2 表\n')
48
+ else:
49
+ n = c.execute('select count(*) from VariableTagDetailsV2').fetchone()[0]
50
+ if not n:
51
+ f.write('\t\t\t本工程变量表为空(控件地址在 Bin/fshmi.bin,见 控件地址.tsv)\n')
52
+
53
+ # 控件地址:Bin/fshmi.bin 是编译后的屏程序,里面是「指令编号 + 地址」流
54
+ # (SW n=开关/位、NE n=数值设定、AE n=字符串、OL n=…,后跟 D420/M45/Y4/SRW426 这类地址)
55
+ import re
56
+ fm = os.path.join(root, 'Bin', 'fshmi.bin')
57
+ addr_rows = []
58
+ if os.path.exists(fm):
59
+ txt = open(fm, 'rb').read().decode('utf-8', errors='ignore')
60
+ pending = None
61
+ for t in re.findall(r'(?:SW|NE|AE|OL|SRB|SRW)\s?\d+|[A-Z]{1,3}\d+', txt):
62
+ if re.match(r'^(SW|NE|AE|OL)\s?\d+$', t):
63
+ pending = t
64
+ elif pending and re.match(r'^[A-Z]{1,3}\d+$', t):
65
+ addr_rows.append((pending, t))
66
+ pending = None
67
+ with open(os.path.join(out, '控件地址.tsv'), 'w', encoding='utf-8') as f:
68
+ f.write('指令编号\t地址\t来源\n')
69
+ for a, b2 in addr_rows:
70
+ f.write(f'{a}\t{b2}\tfshmi.bin\n')
71
+
72
+ print(f"OK {root}")
73
+ print(f" 画面 {len(windows)} / 控件 {len(comps)} / 控件地址 {len(addr_rows)} → {out}")
74
+ return 0
75
+
76
+ if __name__ == '__main__':
77
+ sys.exit(main())