dsh-plugin-pscad-expert-vyma 1.0.13 → 1.0.15

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PSCAD Expert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pscad-dsh-plugin — PSCAD 自动化建模与仿真控制专家插件
2
2
 
3
- > **版本**:1.0.13 | **注册名**:`pscad-automation-expert`
3
+ > **版本**:1.0.15 | **注册名**:`pscad-automation-expert`
4
4
  > **来源**:PSCAD 知识资产固化项目(四阶段:技能文档 → 模板索引 → 工具库 → 插件打包)
5
5
  > 所有知识、工具、模板均来自真实仿真验证(L13~L19 认证轮 + R4 科研轮),**无伪造数据**。
6
6
 
@@ -76,32 +76,44 @@ Agent 维持运行 Loop。全函数 Type Hints + 中文 Docstring。
76
76
 
77
77
  ---
78
78
 
79
- ## 三、在其他电脑的 DSH 中拉取与加载
79
+ ## 三、安装(Install)
80
80
 
81
- ### 1. 前置环境(宿主机器必须已具备)
82
- - PSCAD 5.0.0 Pro(路径以实际安装为准)
83
- - Python 3.7+ 与 mhi.pscad 2.2.1(路径以实际安装为准;首次使用调用 init_pscad_environment 配置)
84
- - 路径不符时,修改 `tools/main.py` 顶部常量:`PSCAD_EXE` / `PSCAD_PORT` / `PYLIB_DIR` / `DEFAULT_FOLDER`
81
+ DSH 组合包(bundle)方式从 npm 安装到目标 profile(正式使用请用 **desktop 激活 profile**,勿用已弃用的 demo):
85
82
 
86
- ### 2. Git 拉取
87
- ```bash
88
- git clone <私有仓库地址>/pscad-dsh-plugin.git
89
- cd pscad-dsh-plugin
83
+ ```powershell
84
+ # 首次安装(dsh 不在 PATH 时用宿主命令全路径调用)
85
+ & "$env:APPDATA\DSH Desktop\host-commands\desktop\bin\dsh.cmd" plugin add dsh-plugin-pscad-expert-vyma@^1.0.14 --profile desktop
90
86
  ```
91
87
 
92
- ### 3. DSH 挂载激活
93
- 1. 将 `pscad-dsh-plugin/` 目录放入 DSH 插件目录;
94
- 2. DSH 读取根目录 `plugin.yaml` 完成注册:
95
- - `entry_points.tools.module = tools.main` → 挂载 11 个工具函数;
96
- - `knowledge_base.preload` → 自动加载两份知识文档为系统提示词前置上下文;
97
- 3. 首次调用前自检:
98
- ```bash
99
- python -c "import sys; sys.path.insert(0, 'tools'); import main; print(main.self_check())"
100
- ```
101
- 4. 激活后 Agent 可直接调用工具;工具会自动拉起/连接 PSCAD(端口 54329),
102
- 自动化服务崩溃时自动 `restart_pscad` 恢复。
103
-
104
- ### 4. 快速上手示例
88
+ 安装会同时写入 profile 的 `dependencies` 与 `dsh.profile.bundles`(两者缺一 GUI 不加载)。重启 DSH Desktop 后,插件的 13 个 `pscad_*` 工具即注册为 Agent 可直接调用的 DSH 工具。
89
+
90
+ > **前置环境**:仅需目标机安装 PSCAD 5.x Python 3.7+。mhi.pscad 由插件自动发现(PSCAD 自带 `bin/win64/mhi.zip`,或 pip 版 `mhi-pscad`),**无需预置任何文件**。
91
+ > **新机器一键安装**:可用随包脚本 `scripts/install_test_machine.ps1`(自动定位宿主命令、安装到 desktop profile、处理供应链策略、自动探测 Python)。
92
+
93
+ ## 四、配置(Configure)
94
+
95
+ 零配置开箱即用;仅以下场景需要配置:
96
+
97
+ | 场景 | 方式 |
98
+ |---|---|
99
+ | 首次告知 PSCAD 安装路径 | 对话中调用 `pscad_init_environment(exe_path="...")`(pylib 可省略,自动发现) |
100
+ | 覆盖默认 Python 解释器 | desktop profile 的 `cordis.patch.yml` 中为 `pscad-expert-bridge` 加 `config.pythonExecutable`(默认自动探测,通常无需) |
101
+ | 调整工具超时 | 同上加 `config.timeoutMs`(默认 300000ms;须大于 PSCAD 冷启动等待 180s) |
102
+
103
+ 配置层序遵循 DSH 标准:**bundle patch → profile patch → 机器级 → CLI overlay**,后层按行覆盖(整 config 替换语义)。
104
+
105
+ ## 五、升级(Upgrade)
106
+
107
+ ```powershell
108
+ # 关闭 DSH Desktop 后执行(官方标准通道:按 package.json 的 ^ 范围解析新版本)
109
+ & "$env:APPDATA\DSH Desktop\host-commands\desktop\bin\dsh.cmd" plugin up dsh-plugin-pscad-expert-vyma --profile desktop
110
+ # 或强制升到最新版:
111
+ & "$env:APPDATA\DSH Desktop\host-commands\desktop\bin\dsh.cmd" plugin up dsh-plugin-pscad-expert-vyma --latest --profile desktop
112
+ ```
113
+
114
+ > 无人值守/一键升级可用随包脚本 `scripts/self_heal_pscad_plugin.ps1`(自动查询 npm 最新版 → 退出 GUI → 更新依赖 → 核验)。误写 patch 配置后的修复见 `scripts/fix_cordis_patch.ps1`。
115
+
116
+ ## 六、快速上手示例(库级调用)
105
117
  ```python
106
118
  import sys
107
119
  sys.path.insert(0, r'<插件路径>\tools')
@@ -122,7 +134,7 @@ r = pat.read_output(channel='VrA', method='rms')
122
134
 
123
135
  ---
124
136
 
125
- ## 四、使用建议(实战教训速查)
137
+ ## 七、使用建议(实战教训速查)
126
138
 
127
139
  - **模板优先**:复杂拓扑基于 `templates/` 二次开发,避免纯代码从零绘制(易坐标越界/悬空)。
128
140
  - **关键实验独立案例**:工程同名重建会残留旧 build 消息——每次用唯一案例名。
@@ -131,7 +143,7 @@ r = pat.read_output(channel='VrA', method='rms')
131
143
  - **DFT 口径**:.out 列 RMS = 相 RMS;基波提取须用系统实际频率(F0)。
132
144
  - 完整排错清单见 `knowledge/pscad_skill_core.md` 模块三。
133
145
 
134
- ## 五、许可证与维护
146
+ ## 八、许可证与维护
135
147
 
136
148
  - 内部资产,仅供团队私有仓库使用。
137
149
  - 知识/工具/模板均来自真实仿真验证,故意异常实验保留真实诊断,禁止伪造/掩盖结果。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-pscad-expert-vyma",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "PSCAD 自动化建模与仿真控制专家插件(DSH Agent 工具库 + 基准模板 + 领域知识)",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -23,6 +23,10 @@
23
23
  ],
24
24
  "author": "PSCAD Expert",
25
25
  "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/jionmark11-hub/PSCAD.git"
29
+ },
26
30
  "files": [
27
31
  "knowledge/",
28
32
  "tools/",
@@ -31,11 +35,22 @@
31
35
  "README.md",
32
36
  "index.js",
33
37
  "cordis.patch.yml",
38
+ "LICENSE",
39
+ "scripts/self_heal_pscad_plugin.ps1",
40
+ "scripts/install_test_machine.ps1",
41
+ "scripts/fix_cordis_patch.ps1",
34
42
  "!tools/__pycache__/",
35
43
  "!**/*.pyc",
36
44
  "!**/*.pyo"
37
45
  ],
46
+ "scripts": {
47
+ "verify:js": "node --check index.js",
48
+ "verify:py": "node scripts/verify-python.mjs",
49
+ "verify": "npm run verify:js && npm run verify:py",
50
+ "prepack": "npm run verify"
51
+ },
38
52
  "engines": {
53
+ "node": ">=18",
39
54
  "python": ">=3.7"
40
55
  }
41
56
  }
package/plugin.yaml CHANGED
@@ -3,7 +3,7 @@
3
3
  # 由知识资产固化项目自动生成(2026-08-25)· 部署至 Git 仓库
4
4
  # ============================================================
5
5
  name: pscad-automation-expert
6
- version: 1.0.13
6
+ version: 1.0.15
7
7
  description: PSCAD 自动化建模与仿真控制专家插件
8
8
 
9
9
  # ---- 系统提示词(加载到 Agent 上下文最前置位置)----
@@ -0,0 +1,59 @@
1
+ # ============================================================
2
+ # 修复 cordis.patch.yml(被一键安装脚本误追加导致 DSH Desktop 崩溃)
3
+ # 用法:保存为 fix_cordis.ps1,PowerShell 中运行:
4
+ # powershell -ExecutionPolicy Bypass -File fix_cordis.ps1
5
+ # ============================================================
6
+
7
+ $ErrorActionPreference = 'Stop'
8
+ try { chcp 65001 | Out-Null } catch {}
9
+
10
+ $patchFile = "$env:USERPROFILE\.dsh\profiles\desktop\cordis.patch.yml"
11
+ if (-not (Test-Path -LiteralPath $patchFile)) {
12
+ Write-Host "!! 未找到 $patchFile" -ForegroundColor Red
13
+ exit 1
14
+ }
15
+
16
+ # 1) 备份损坏文件
17
+ $bak = "$patchFile.broken-$(Get-Date -Format yyyyMMdd-HHmmss)"
18
+ Copy-Item -LiteralPath $patchFile -Destination $bak -Force
19
+ Write-Host "已备份损坏文件 -> $bak" -ForegroundColor Cyan
20
+
21
+ # 2) 从损坏文件里找回已写入的 pythonExecutable(若存在)
22
+ $py = $null
23
+ $broken = Get-Content -LiteralPath $patchFile -Raw
24
+ if ($broken -match "pythonExecutable:\s*'([^']+)'") { $py = $matches[1] }
25
+ if (-not $py) {
26
+ # 重新自动发现 Python 3.7+
27
+ try {
28
+ $pyOut = py -0p 2>$null
29
+ foreach ($line in $pyOut) {
30
+ if ($line -match '(\S+\\python\.exe)\s*$') {
31
+ $cand = $matches[1]
32
+ if (Test-Path -LiteralPath $cand) {
33
+ $ver = (& $cand -c "import sys; print('%d.%d' % sys.version_info[:2])" 2>$null | Select-Object -First 1)
34
+ if ($ver -and [version]$ver -ge [version]'3.7') { $py = $cand; break }
35
+ }
36
+ }
37
+ }
38
+ } catch {}
39
+ }
40
+ if (-not $py) {
41
+ $py = Read-Host "未自动找到 Python 3.7+,请手动输入其绝对路径(如 C:/Program Files/Python37/python.exe)"
42
+ if (-not $py) { Write-Host "!! 未提供路径,中止。" -ForegroundColor Red; exit 1 }
43
+ }
44
+ $py = $py.Replace('\', '/')
45
+
46
+ # 3) 恢复为原始占位格式(注释 + [])。1.0.9 起插件自动探测 Python,无需任何覆盖配置
47
+ $template = @'
48
+ # Your patch layer for this dsh profile, applied after every bundle layer:
49
+ # a top-level YAML array of loader patch entries (id-targeted config
50
+ # overrides, disables, and insert lists; `!!js` expressions allowed).
51
+ []
52
+ '@
53
+ [System.IO.File]::WriteAllText($patchFile, $template, (New-Object System.Text.UTF8Encoding($true)))
54
+
55
+ Write-Host ""
56
+ Write-Host "已修复,新内容如下:" -ForegroundColor Green
57
+ Get-Content -LiteralPath $patchFile
58
+ Write-Host ""
59
+ Write-Host "现在重新打开 DSH Desktop,应能正常启动。若仍报错,请把报错原文发回。" -ForegroundColor Yellow
@@ -0,0 +1,133 @@
1
+ # ============================================================
2
+ # PSCAD 插件 dsh-plugin-pscad-expert-vyma@1.0.8 测试端一键安装脚本
3
+ # 用途:安装到 desktop(正常激活)profile + 自动配置 pythonExecutable
4
+ # 前提:本机已安装 DSH Desktop、PSCAD 5.x、Python 3.7+
5
+ # 重要:请先关闭 DSH Desktop GUI,再运行本脚本
6
+ # ============================================================
7
+
8
+ $ErrorActionPreference = 'Continue'
9
+ try { chcp 65001 | Out-Null } catch {}
10
+
11
+ Write-Host ""
12
+ Write-Host "================ PSCAD 插件 1.0.8 自动安装 ================" -ForegroundColor Cyan
13
+ Write-Host "请确认:DSH Desktop GUI 已关闭?(本脚本会修改 profile 依赖目录)" -ForegroundColor Yellow
14
+ $null = Read-Host "按回车继续,或 Ctrl+C 取消"
15
+
16
+ # ---------- 第 1 步:定位 dsh 宿主命令 ----------
17
+ Write-Host ""
18
+ Write-Host "[1/4] 定位 dsh 宿主命令..." -ForegroundColor Cyan
19
+ $dsh = "$env:APPDATA\DSH Desktop\host-commands\desktop\bin\dsh.cmd"
20
+ if (-not (Test-Path -LiteralPath $dsh)) {
21
+ $found = Get-ChildItem "$env:APPDATA\DSH Desktop", "$env:LOCALAPPDATA", "C:\Program Files", "D:\" -Recurse -Filter 'dsh.cmd' -ErrorAction SilentlyContinue | Select-Object -First 1
22
+ if ($found) { $dsh = $found.FullName } else {
23
+ Write-Host "!! 未找到 dsh.cmd:请确认 DSH Desktop 已正确安装。" -ForegroundColor Red
24
+ exit 1
25
+ }
26
+ }
27
+ Write-Host "dsh = $dsh"
28
+
29
+ # ---------- 第 2 步:安装插件到 desktop profile ----------
30
+ Write-Host ""
31
+ Write-Host "[2/4] 安装 dsh-plugin-pscad-expert-vyma@1.0.8 到 desktop profile..." -ForegroundColor Cyan
32
+ & $dsh plugin add "dsh-plugin-pscad-expert-vyma@^1.0.8" --profile desktop
33
+ if ($LASTEXITCODE -ne 0) {
34
+ Write-Host "安装失败(退出码 $LASTEXITCODE),尝试处理 pnpm 供应链策略..." -ForegroundColor Yellow
35
+ $ws = "$env:USERPROFILE\.dsh\profiles\desktop\pnpm-workspace.yaml"
36
+ if (Test-Path -LiteralPath $ws) {
37
+ Copy-Item -LiteralPath $ws -Destination "$ws.bak-$(Get-Date -Format yyyyMMdd-HHmmss)" -Force
38
+ Add-Content -LiteralPath $ws -Value "`nminimumReleaseAge: 0"
39
+ Write-Host "已备份并追加 minimumReleaseAge: 0,重试..."
40
+ & $dsh plugin add "dsh-plugin-pscad-expert-vyma@^1.0.8" --profile desktop
41
+ } else {
42
+ Write-Host "!! 未找到 pnpm-workspace.yaml,无法自动处理,请把报错发回。" -ForegroundColor Red
43
+ }
44
+ }
45
+
46
+ # ---------- 第 3 步:自动发现 Python 并配置 pythonExecutable ----------
47
+ Write-Host ""
48
+ Write-Host "[3/4] 自动发现真实 Python 3.7+ 并写入 desktop profile 补丁..." -ForegroundColor Cyan
49
+ $py = $null
50
+ # 3a) 用 py 启动器枚举已安装版本
51
+ try {
52
+ $pyOut = py -0p 2>$null
53
+ foreach ($line in $pyOut) {
54
+ if ($line -match '(\S+\\python\.exe)\s*$') {
55
+ $cand = $matches[1]
56
+ if (Test-Path -LiteralPath $cand) {
57
+ $ver = (& $cand -c "import sys; print('%d.%d' % sys.version_info[:2])" 2>$null | Select-Object -First 1)
58
+ if ($ver -and [version]$ver -ge [version]'3.7') { $py = $cand; break }
59
+ }
60
+ }
61
+ }
62
+ } catch {}
63
+ # 3b) 常见安装路径兜底
64
+ if (-not $py) {
65
+ foreach ($c in @("C:\Program Files\Python37\python.exe",
66
+ "$env:LOCALAPPDATA\Programs\Python\Python3*\python.exe",
67
+ "C:\Python3*\python.exe")) {
68
+ $hit = Get-Item $c -ErrorAction SilentlyContinue | Select-Object -First 1
69
+ if ($hit) { $py = $hit.FullName; break }
70
+ }
71
+ }
72
+ if ($py) {
73
+ Write-Host "发现 Python: $py"
74
+ $patchFile = "$env:USERPROFILE\.dsh\profiles\desktop\cordis.patch.yml"
75
+ if (Test-Path -LiteralPath $patchFile) {
76
+ $pySlash = $py.Replace('\', '/')
77
+ $entry = @"
78
+ - id: pscad-expert-bridge
79
+ config:
80
+ pythonExecutable: '$pySlash'
81
+ "@
82
+ $raw = Get-Content -LiteralPath $patchFile -Raw
83
+ $hasEntry = $raw -match 'pscad-expert-bridge'
84
+ $bodyOnly = ($raw -replace '#[^\r\n]*', '').Trim()
85
+ if ($hasEntry) {
86
+ Write-Host "!! cordis.patch.yml 已有 pscad-expert-bridge 条目,请手动补充:" -ForegroundColor Yellow
87
+ Write-Host " config:" -ForegroundColor Yellow
88
+ Write-Host " pythonExecutable: '$pySlash'" -ForegroundColor Yellow
89
+ } elseif ($bodyOnly -in @('', '[]')) {
90
+ Copy-Item -LiteralPath $patchFile -Destination "$patchFile.bak-$(Get-Date -Format yyyyMMdd-HHmmss)" -Force
91
+ if ($bodyOnly -eq '[]') {
92
+ # 把 [] 占位行替换为条目(保持注释)
93
+ $new = [regex]::Replace($raw, '^\s*\[\]\s*$', $entry, 'Multiline')
94
+ } else {
95
+ # 文件为空或仅注释:直接写入条目
96
+ $new = $entry
97
+ }
98
+ [System.IO.File]::WriteAllText($patchFile, $new, (New-Object System.Text.UTF8Encoding($true)))
99
+ Write-Host "已备份并写入 pythonExecutable 配置 ✔"
100
+ } else {
101
+ Write-Host "!! cordis.patch.yml 含有其他内容且无 [],为安全起见请手动添加以下条目:" -ForegroundColor Yellow
102
+ Write-Host $entry -ForegroundColor Yellow
103
+ }
104
+ } else {
105
+ Write-Host "!! 未找到 $patchFile,请确认 desktop profile 存在。" -ForegroundColor Red
106
+ }
107
+ } else {
108
+ Write-Host "!! 未自动找到 Python 3.7+:请先安装 Python,或在 cordis.patch.yml 手动配置 pythonExecutable。" -ForegroundColor Red
109
+ }
110
+
111
+ # ---------- 第 4 步:安装结果核验 ----------
112
+ Write-Host ""
113
+ Write-Host "[4/4] 核验安装结果..." -ForegroundColor Cyan
114
+ $pkgJson = "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\dsh-plugin-pscad-expert-vyma\package.json"
115
+ if (Test-Path -LiteralPath $pkgJson) {
116
+ $ver = (Get-Content -LiteralPath $pkgJson -Raw | ConvertFrom-Json).version
117
+ Write-Host "已安装插件版本: $ver"
118
+ } else {
119
+ Write-Host "!! 未在 desktop profile 找到插件(node_modules 检查失败),请把上方输出发回排查。" -ForegroundColor Red
120
+ }
121
+
122
+ # ---------- 下一步(在 DSH 对话中完成) ----------
123
+ Write-Host ""
124
+ Write-Host "================ 完成,接下来(请照做) ================" -ForegroundColor Green
125
+ Write-Host "1) 关闭本 PowerShell 窗口,重新打开 DSH Desktop(激活 profile 保持 desktop)"
126
+ Write-Host "2) 打开 DSH 对话,粘贴执行下面两条指令(exe_path 换成你机器上 PSCAD 的实际路径):"
127
+ Write-Host ""
128
+ Write-Host ' pscad_init_environment(exe_path="D:/PSCAD/bin/win64/Pscad.exe")' -ForegroundColor White
129
+ Write-Host ' pscad_self_check' -ForegroundColor White
130
+ Write-Host ""
131
+ Write-Host "预期结果:mhi_import: true、pylib_source: auto-discovered(或 config)、pscad_exe_exists: true"
132
+ Write-Host "若哪一步报错,请把完整报错原文发回。" -ForegroundColor Yellow
133
+ Write-Host ""
@@ -0,0 +1,104 @@
1
+ # ============================================================
2
+ # PSCAD 插件自愈升级脚本:自动升到 npm 最新版
3
+ # 用法:powershell -ExecutionPolicy Bypass -File self_heal_pscad_plugin.ps1
4
+ # 流程:查 npm 最新版 → 比对已装版 → 彻底退出 DSH Desktop →
5
+ # 自动改写 profile package.json 版本号并确保 bundles → 安装依赖 → 核验
6
+ # 说明:脚本会关闭 DSH Desktop(必要时),运行完请重新打开。
7
+ # ============================================================
8
+
9
+ $ErrorActionPreference = 'Stop'
10
+ try { chcp 65001 | Out-Null } catch {}
11
+
12
+ $pkg = 'dsh-plugin-pscad-expert-vyma'
13
+ $profileDir = "$env:USERPROFILE\.dsh\profiles\desktop"
14
+ $pkgJson = "$profileDir\package.json"
15
+
16
+ Write-Host ""
17
+ Write-Host "======== PSCAD 插件自愈升级 ========" -ForegroundColor Cyan
18
+
19
+ # [1/6] 查询 npm 最新版本
20
+ Write-Host "[1/6] 查询 npm 最新版本..." -ForegroundColor Cyan
21
+ $latest = (Invoke-RestMethod -Uri "https://registry.npmjs.org/$pkg/latest" -TimeoutSec 30).version
22
+ Write-Host "最新版本: $latest"
23
+
24
+ # [2/6] 检查已装版本
25
+ Write-Host "[2/6] 检查已装版本..." -ForegroundColor Cyan
26
+ $installed = $null
27
+ if (Test-Path "$profileDir\node_modules\$pkg\package.json") {
28
+ $installed = (Get-Content "$profileDir\node_modules\$pkg\package.json" -Raw | ConvertFrom-Json).version
29
+ }
30
+ if ($null -eq $installed) { Write-Host "已装版本: 未安装" } else { Write-Host "已装版本: $installed" }
31
+
32
+ if ($installed -eq $latest) {
33
+ Write-Host "已是最新版本,无需升级 ✔" -ForegroundColor Green
34
+ Write-Host "======================================"
35
+ exit 0
36
+ }
37
+
38
+ # [3/6] 彻底退出 DSH Desktop(Electron 关窗口只是隐藏,后台进程仍在)
39
+ Write-Host "[3/6] 退出 DSH Desktop(若在运行)..." -ForegroundColor Cyan
40
+ $procs = Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -match '^DSH' }
41
+ foreach ($p in $procs) {
42
+ Write-Host " 结束进程: $($p.ProcessName) (PID $($p.Id))"
43
+ Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
44
+ }
45
+ Start-Sleep -Seconds 2
46
+
47
+ # [4/6] 备份并改写 profile package.json(版本号 + 确保 bundles)
48
+ Write-Host "[4/6] 更新 profile package.json..." -ForegroundColor Cyan
49
+ if (-not (Test-Path -LiteralPath $pkgJson)) {
50
+ Write-Host "!! 未找到 $pkgJson,请确认 DSH Desktop 已正确安装。" -ForegroundColor Red
51
+ exit 1
52
+ }
53
+ Copy-Item -LiteralPath $pkgJson -Destination "$pkgJson.bak-$(Get-Date -Format yyyyMMdd-HHmmss)" -Force
54
+ $text = [System.IO.File]::ReadAllText($pkgJson, [System.Text.Encoding]::UTF8)
55
+
56
+ # 4a) 依赖版本号:无论当前写的是什么,统一替换为 ^最新(^ 范围让后续 dsh plugin up 可解析新版本)
57
+ $new = $text -replace '("dsh-plugin-pscad-expert-vyma"\s*:\s*")[^"]*(")', ('${1}^' + $latest + '${2}')
58
+ if ($new -notmatch ('"' + $pkg + '"\s*:\s*"\^?' + [regex]::Escape($latest) + '"')) {
59
+ Write-Host " !! 未在 dependencies 找到插件条目,无法自动改版本,请先把上方输出发回。" -ForegroundColor Red
60
+ exit 1
61
+ }
62
+ # 4b) 确保 dsh.profile.bundles 包含插件名(缺则补上)
63
+ try {
64
+ $json = $new | ConvertFrom-Json
65
+ if ($null -ne $json.dsh.profile.bundles -and $json.dsh.profile.bundles -notcontains $pkg) {
66
+ $json.dsh.profile.bundles += $pkg
67
+ $new = $json | ConvertTo-Json -Depth 10
68
+ Write-Host " bundles 缺少插件名,已补上 ✔"
69
+ } else {
70
+ Write-Host " bundles 已包含插件名 ✔"
71
+ }
72
+ } catch {
73
+ Write-Host " !! JSON 解析失败:$($_.Exception.Message)" -ForegroundColor Red
74
+ exit 1
75
+ }
76
+ [System.IO.File]::WriteAllText($pkgJson, $new, (New-Object System.Text.UTF8Encoding($false)))
77
+ Write-Host " package.json 已更新(备份在 .bak-* 文件)✔"
78
+
79
+ # [5/6] 安装依赖
80
+ Write-Host "[5/6] 安装依赖..." -ForegroundColor Cyan
81
+ Set-Location -LiteralPath $profileDir
82
+ if (Get-Command pnpm -ErrorAction SilentlyContinue) {
83
+ pnpm install
84
+ } elseif (Get-Command npm -ErrorAction SilentlyContinue) {
85
+ npm install
86
+ } else {
87
+ Write-Host "!! 未找到 pnpm/npm,无法安装依赖。" -ForegroundColor Red
88
+ exit 1
89
+ }
90
+ if ($LASTEXITCODE -ne 0) {
91
+ Write-Host "!! 安装失败(退出码 $LASTEXITCODE),请把上方输出发回。" -ForegroundColor Red
92
+ exit 1
93
+ }
94
+
95
+ # [6/6] 核验
96
+ Write-Host "[6/6] 核验..." -ForegroundColor Cyan
97
+ $now = (Get-Content "$profileDir\node_modules\$pkg\package.json" -Raw | ConvertFrom-Json).version
98
+ if ($now -eq $latest) {
99
+ Write-Host "升级成功:$now ✔" -ForegroundColor Green
100
+ } else {
101
+ Write-Host "!! 核验失败:实际为 $now(期望 $latest),请把上方输出发回。" -ForegroundColor Red
102
+ }
103
+ Write-Host "======================================"
104
+ Write-Host "现在重新打开 DSH Desktop 即可使用最新版。"
package/tools/main.py CHANGED
@@ -55,6 +55,7 @@ from __future__ import annotations
55
55
  import json
56
56
  import os
57
57
  import re
58
+ import socket
58
59
  import sys
59
60
  import time
60
61
  import math
@@ -668,6 +669,21 @@ def connect_and_load_project(project_name: str,
668
669
  return _err(exc)
669
670
 
670
671
 
672
+ def _port_ready(port: int, timeout_s: float = 1.0) -> bool:
673
+ """毫秒~秒级 TCP 端口探测:PSCAD 自动化端口是否已监听。
674
+
675
+ 背景(P5 实测):mhi-pscad 2.9.7 的 connect(timeout=5) 在 PSCAD 未运行时会
676
+ 内部重试阻塞 ~16s,不尊重超时参数;轮询循环若直接调 connect,单次迭代
677
+ ≈18s,180s 名义上限实际只能尝试约 10 次。改用 socket 探测后单次迭代 ≈2s,
678
+ 且 connect 只在端口就绪后才调用。
679
+ """
680
+ try:
681
+ with socket.create_connection(('127.0.0.1', port), timeout=timeout_s):
682
+ return True
683
+ except Exception:
684
+ return False
685
+
686
+
671
687
  def _ensure_connected() -> Tuple[bool, str]:
672
688
  """确保存在活的 mhi 连接(幂等;懒加载 mhi)。"""
673
689
  global _pscad
@@ -680,6 +696,9 @@ def _ensure_connected() -> Tuple[bool, str]:
680
696
  return True, ''
681
697
  except Exception:
682
698
  _pscad = None
699
+ # 端口未监听时直接快速失败(避免 mhi connect 阻塞 ~16s)
700
+ if not _port_ready(PSCAD_PORT, 0.5):
701
+ return False, 'PSCAD 自动化端口 %d 未就绪(PSCAD 未运行?)' % PSCAD_PORT
683
702
  try:
684
703
  _pscad = mhi.pscad.connect(port=PSCAD_PORT, timeout=CONNECT_TIMEOUT_S)
685
704
  return True, ''
@@ -760,7 +779,13 @@ def _ensure_project() -> Optional[Dict[str, Any]]:
760
779
 
761
780
 
762
781
  def _launch_pscad() -> Tuple[bool, str]:
763
- """拉起 PSCAD 进程并等待自动化端口就绪(崩溃恢复标准路径;懒加载 exe 路径)。"""
782
+ """拉起 PSCAD 进程并等待自动化端口就绪(崩溃恢复标准路径;懒加载 exe 路径)。
783
+
784
+ 轮询用 socket 端口探测(毫秒级)而非直接 mhi.pscad.connect:P5 实测
785
+ mhi 2.9.7 的 connect(timeout=5) 在 PSCAD 未起时阻塞 ~16s(不尊重超时),
786
+ 直接调用会让 180s 名义上限实际只能尝试约 10 次;socket 探测使单次迭代
787
+ ≈2s,端口就绪后才调用 connect 建立自动化连接。
788
+ """
764
789
  global _pscad
765
790
  exe = _cfg_exe()
766
791
  if not exe:
@@ -770,12 +795,14 @@ def _launch_pscad() -> Tuple[bool, str]:
770
795
  '/port:%d' % PSCAD_PORT, '/splash:false'])
771
796
  deadline = time.time() + LAUNCH_WAIT_S
772
797
  while time.time() < deadline:
798
+ if _port_ready(PSCAD_PORT, 1.0):
799
+ try:
800
+ _pscad = mhi.pscad.connect(port=PSCAD_PORT,
801
+ timeout=CONNECT_TIMEOUT_S)
802
+ return True, ''
803
+ except Exception:
804
+ continue
773
805
  time.sleep(2)
774
- try:
775
- _pscad = mhi.pscad.connect(port=PSCAD_PORT, timeout=5)
776
- return True, ''
777
- except Exception:
778
- continue
779
806
  return False, 'PSCAD 启动后 %ds 内端口未就绪' % LAUNCH_WAIT_S
780
807
  except Exception as exc:
781
808
  return False, str(exc)