poker-mcp 1.7.2 → 1.8.2
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.md +79 -14
- package/package.json +6 -1
- package/src/mcp/handlers/cadHandlers.js +187 -0
- package/src/mcp/handlers/calculationHandlers.js +2 -1
- package/src/mcp/handlers/index.js +4 -2
- package/src/mcp/tools/cadTools.js +65 -0
- package/src/mcp/tools/calculationTools.js +5 -1
- package/src/mcp/tools/index.js +6 -3
- package/src/services/CalculationService.js +16 -5
- package/src/utils/paths.js +83 -0
- package/tools/audit_mfp.py +174 -0
- package/tools/compare_poker_trace.py +140 -0
- package/tools/gen_paths.py +470 -0
- package/tools/poker_lib.py +172 -0
- package/tools/ray_trace_tri.py +296 -0
package/README.md
CHANGED
|
@@ -11,6 +11,38 @@ YAML-based input file management tool for radiation-shielding calculation code P
|
|
|
11
11
|
- **核種データ**: `${POKER_INSTALL_PATH}/LIB/ICRP-07.NDX` を直接参照
|
|
12
12
|
- **実行方式**: STDIO通信(MCPプロトコル標準)
|
|
13
13
|
|
|
14
|
+
## 🆕 バージョン1.8.0〜1.8.1の新機能
|
|
15
|
+
|
|
16
|
+
### 📐 CAD連携が MCP だけで完結
|
|
17
|
+
従来、CAD からの経路抽出は MCP を経由せず、FreeCAD を手で起動する必要が
|
|
18
|
+
ありました。`spec.json` を手書きし、`poker_cui -p` を叩き、`freecadcmd` に
|
|
19
|
+
スクリプトを渡す。3 手順すべてが手作業でした。
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
poker_generatePaths({ fcstd: "C:/path/to/model.FCStd" })
|
|
23
|
+
poker_executeCalculation({ yaml_file: "poker.yaml", path_input: "poker.paths" })
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
CAD ファイルを指定するだけになりました。分割点の取得、グリッド検出器の展開、
|
|
27
|
+
ビルドアップ等価材料の解決(`Source_Dry` → `Tungsten` など)は自動です。
|
|
28
|
+
|
|
29
|
+
### 🔍 FREECAD_PATH
|
|
30
|
+
FreeCAD の場所を、環境変数 → PATH → 既定のインストール先の順で解決します。
|
|
31
|
+
`POKER_INSTALL_PATH` のように固定の既定値を持てないのは、インストール先が
|
|
32
|
+
バージョン番号を含むためです(FreeCAD 1.1, 1.0 …)。
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
"env": {
|
|
36
|
+
"POKER_INSTALL_PATH": "C:\\Poker",
|
|
37
|
+
"FREECAD_PATH": "C:\\Program Files\\FreeCAD 1.1"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 🐛 相対パスの解決を修正
|
|
42
|
+
`tasks/` 配下のファイル名指定が、プロセスのカレントディレクトリを基準に
|
|
43
|
+
解決されていました。MCP サーバの起動場所によっては別の場所を指します。
|
|
44
|
+
|
|
45
|
+
|
|
14
46
|
## 🆕 バージョン1.7.0〜1.7.2の新機能
|
|
15
47
|
|
|
16
48
|
### 📉 サマリー出力量の制御(ThinnedIndices 操作系 3メソッド)
|
|
@@ -72,28 +104,61 @@ poker_getThinnedIndices()
|
|
|
72
104
|
|
|
73
105
|
### 📡 CAD連携レイトレース
|
|
74
106
|
FreeCAD のソリッドモデルを、STEP のような中間フォーマットを介さず、CSG で
|
|
75
|
-
|
|
76
|
-
追跡し、通過した材質と厚さを `.paths` ファイルで POKER に渡します。
|
|
107
|
+
表現し直す作業も無しに遮蔽体系として扱えます。**MCP の 2 呼び出しで完結**します。
|
|
77
108
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
poker_cui model.yaml --path-input model.paths -t # 計算
|
|
109
|
+
```javascript
|
|
110
|
+
poker_generatePaths({ fcstd: "C:/path/to/model.FCStd" })
|
|
111
|
+
poker_executeCalculation({ yaml_file: "poker.yaml", path_input: "poker.paths" })
|
|
82
112
|
```
|
|
83
113
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
114
|
+
線源点→検出器の直線を FreeCAD 側で追跡し、通過した材質と厚さを `.paths` に
|
|
115
|
+
記録して POKER に渡します。分割点の取得、グリッド検出器の展開、ビルドアップ
|
|
116
|
+
等価材料の解決はすべて自動です。
|
|
117
|
+
|
|
118
|
+
CSG 経由と一致することを平板・円筒・球の 5 体系で確認済みです。フィレットや
|
|
119
|
+
自由曲面のように CSG で表現できない形状も扱えます。**複数線源・グリッド検出器・
|
|
120
|
+
スラント補正に対応**。
|
|
87
121
|
|
|
88
122
|
他の CAD で作ったモデルも、STEP で FreeCAD に読み込めば同じパイプラインに
|
|
89
123
|
乗ります。副産物として、POKER の改造なしで使える**簡易化の監査ツール**
|
|
90
124
|
(ある形状を省略してよいかを定量化する)もあります。
|
|
91
125
|
|
|
126
|
+
FreeCAD の場所は環境変数 `FREECAD_PATH` で指定します(未設定なら既定の
|
|
127
|
+
インストール先を探索)。
|
|
128
|
+
|
|
129
|
+
#### 必要なもの
|
|
130
|
+
|
|
131
|
+
| | 入手 | 環境変数 |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| poker-mcp | `npm install poker-mcp` | — |
|
|
134
|
+
| **POKER 本体** | 別途入手 | `POKER_INSTALL_PATH` |
|
|
135
|
+
| **FreeCAD** | [freecad.org](https://www.freecad.org/)(無償) | `FREECAD_PATH` |
|
|
136
|
+
|
|
137
|
+
numpy はレイトレーサが使いますが、FreeCAD に同梱されているので追加の
|
|
138
|
+
インストールは不要です。
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
"env": {
|
|
142
|
+
"POKER_MCP_HOME": "C:\\Users\\yoshi\\poker_mcp_workspace",
|
|
143
|
+
"POKER_INSTALL_PATH": "C:\\Poker",
|
|
144
|
+
"FREECAD_PATH": "C:\\Program Files\\FreeCAD 1.1"
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`FREECAD_PATH` は既定の場所にインストールされていれば省略できます。
|
|
149
|
+
|
|
150
|
+
#### モデル側の約束
|
|
151
|
+
|
|
152
|
+
**ソリッドに `PokerMaterial` プロパティで材質名を設定**しておく必要があります
|
|
153
|
+
(`Iron`、`Concrete` など、POKER の材料ライブラリの名前)。密度を上書きする場合は
|
|
154
|
+
`PokerDensity` も設定します。
|
|
155
|
+
|
|
156
|
+
他の CAD で作ったモデルを STEP で読み込んだ場合、材質情報は失われるので
|
|
157
|
+
FreeCAD 上で設定してください。一度設定すれば `.FCStd` に保存されます。
|
|
158
|
+
|
|
92
159
|
詳細は [CAD_RAYTRACE.md](./docs/manuals/CAD_RAYTRACE.md)、フォーマットは
|
|
93
160
|
[PATHS_FORMAT.md](./docs/manuals/PATHS_FORMAT.md)。
|
|
94
161
|
|
|
95
|
-
詳細は [CHANGELOG.md](./CHANGELOG.md) を参照。
|
|
96
|
-
|
|
97
162
|
## 🆕 バージョン1.5.0の新機能
|
|
98
163
|
|
|
99
164
|
### 🖥️ POKER GUI を閉じずに表示を切り替え
|
|
@@ -336,7 +401,7 @@ Claude Desktopで以下のようにテストできます:
|
|
|
336
401
|
## 🏆 主要機能
|
|
337
402
|
|
|
338
403
|
### ✅ **MCP完全対応**
|
|
339
|
-
- **
|
|
404
|
+
- **34メソッド完全実装**: 全ての放射線遮蔽計算入力管理機能
|
|
340
405
|
- **JSON-RPC 2.0準拠**: 標準プロトコル完全対応
|
|
341
406
|
- **STDIO通信**: MCPクライアントとの標準通信方式
|
|
342
407
|
- **自動バックアップ・ロールバック**: 企業品質のデータ保護
|
|
@@ -355,7 +420,7 @@ Claude Desktopで以下のようにテストできます:
|
|
|
355
420
|
|
|
356
421
|
## 🎯 API構成
|
|
357
422
|
|
|
358
|
-
### 🔧 **
|
|
423
|
+
### 🔧 **34メソッド完全実装**
|
|
359
424
|
|
|
360
425
|
| **カテゴリ** | **メソッド数** | **機能** | **主要操作** |
|
|
361
426
|
|-------------|---------------|----------|-------------|
|
|
@@ -368,7 +433,7 @@ Claude Desktopで以下のようにテストできます:
|
|
|
368
433
|
| **📏 Unit** | 5個 | 単位設定管理 | propose・get・update・validateIntegrity・analyzeConversion |
|
|
369
434
|
| **⚙️ System** | 6個 | システム制御 | applyChanges・executeCalculation・resetYaml・confirmDaughterNuclides・openGui・各種検証 |
|
|
370
435
|
|
|
371
|
-
### 📋 **全
|
|
436
|
+
### 📋 **全34メソッド一覧**
|
|
372
437
|
```
|
|
373
438
|
Body系 (3): poker_proposeBody, poker_updateBody, poker_deleteBody
|
|
374
439
|
Zone系 (3): poker_proposeZone, poker_updateZone, poker_deleteZone
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poker-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "YAML-based input file management tool for radiation shielding calculation code POKER with MCP support - Enhanced with collision detection, daughter nuclide auto-completion, and environment variable support",
|
|
5
5
|
"main": "src/mcp_server_stdio_v4.js",
|
|
6
6
|
"type": "module",
|
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
},
|
|
51
51
|
"files": [
|
|
52
52
|
"src/",
|
|
53
|
+
"tools/gen_paths.py",
|
|
54
|
+
"tools/ray_trace_tri.py",
|
|
55
|
+
"tools/poker_lib.py",
|
|
56
|
+
"tools/audit_mfp.py",
|
|
57
|
+
"tools/compare_poker_trace.py",
|
|
53
58
|
"README.md",
|
|
54
59
|
".mcp.json"
|
|
55
60
|
]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// mcp/handlers/cadHandlers.js
|
|
2
|
+
//
|
|
3
|
+
// CAD 連携レイトレース。従来は次の 3 手順を手で行う必要があった。
|
|
4
|
+
// 1. spec.json を手書き(絶対パスを 4 箇所)
|
|
5
|
+
// 2. poker_cui -p で分割点と評価点を出す
|
|
6
|
+
// 3. freecadcmd を起動して gen_paths.py を走らせる
|
|
7
|
+
// このハンドラがまとめて行う。
|
|
8
|
+
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import fs from 'fs/promises';
|
|
11
|
+
import fssync from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { FREECAD_CMD, CAD_TOOLS_DIR, POKER_INSTALL_DIR, TASKS_DIR, YAML_FILE }
|
|
14
|
+
from '../../utils/paths.js';
|
|
15
|
+
import { logger } from '../../utils/logger.js';
|
|
16
|
+
import { MaterialCatalog } from '../../utils/MaterialCatalog.js';
|
|
17
|
+
|
|
18
|
+
function run(cmd, args, opts = {}) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const p = spawn(cmd, args, { ...opts, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
21
|
+
let out = '', err = '';
|
|
22
|
+
p.stdout.on('data', d => out += d);
|
|
23
|
+
p.stderr.on('data', d => err += d);
|
|
24
|
+
p.on('error', e => resolve({ code: -1, out, err: e.message }));
|
|
25
|
+
p.on('close', code => resolve({ code, out, err }));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createCadHandlers(taskManager) {
|
|
30
|
+
return {
|
|
31
|
+
async generatePaths(args) {
|
|
32
|
+
const fail = (msg, hint) => ({
|
|
33
|
+
content: [{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify({ success: false, error: msg, hint }, null, 2)
|
|
36
|
+
}],
|
|
37
|
+
isError: true
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// --- FreeCAD の所在 ---
|
|
41
|
+
if (!FREECAD_CMD) {
|
|
42
|
+
return fail(
|
|
43
|
+
'FreeCAD が見つかりません',
|
|
44
|
+
'環境変数 FREECAD_PATH に freecadcmd の場所を設定してください。' +
|
|
45
|
+
'実行ファイル(C:/Program Files/FreeCAD 1.1/bin/freecadcmd.exe)でも、' +
|
|
46
|
+
'インストールフォルダ(C:/Program Files/FreeCAD 1.1)でも指定できます。' +
|
|
47
|
+
'claude_desktop_config.json の env に追加します。');
|
|
48
|
+
}
|
|
49
|
+
if (!args || !args.fcstd) return fail('fcstd を指定してください');
|
|
50
|
+
if (!fssync.existsSync(args.fcstd))
|
|
51
|
+
return fail('CAD ファイルが見つかりません: ' + args.fcstd);
|
|
52
|
+
|
|
53
|
+
const yamlFile = YAML_FILE;
|
|
54
|
+
if (!fssync.existsSync(yamlFile))
|
|
55
|
+
return fail('入力 YAML がありません: ' + yamlFile,
|
|
56
|
+
'applyChanges で入力を確定してから実行してください。');
|
|
57
|
+
|
|
58
|
+
// --- 1. poker_cui -p で分割点と評価点を出す ---
|
|
59
|
+
// .paths には全点が必要。間引かれていると gen_paths.py がエラーにする。
|
|
60
|
+
const exe = path.join(POKER_INSTALL_DIR, 'poker_cui.EXE');
|
|
61
|
+
if (!fssync.existsSync(exe))
|
|
62
|
+
return fail('poker_cui が見つかりません: ' + exe,
|
|
63
|
+
'環境変数 POKER_INSTALL_PATH を確認してください。');
|
|
64
|
+
|
|
65
|
+
const summary = yamlFile + '.summary';
|
|
66
|
+
const r1 = await run(exe, [yamlFile, '-p', '-t'], { cwd: TASKS_DIR });
|
|
67
|
+
if (r1.code !== 0 || !fssync.existsSync(summary)) {
|
|
68
|
+
return fail('poker_cui -p の実行に失敗しました(終了コード ' + r1.code + ')',
|
|
69
|
+
(r1.err || r1.out || '').slice(-400));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- 2. spec を組み立てる ---
|
|
73
|
+
const out = args.output || path.join(TASKS_DIR, 'poker.paths');
|
|
74
|
+
const spec = {
|
|
75
|
+
fcstd: args.fcstd.replace(/\\/g, '/'),
|
|
76
|
+
out: out.replace(/\\/g, '/'),
|
|
77
|
+
poker_summary: summary.replace(/\\/g, '/'),
|
|
78
|
+
detectors_from_summary: true,
|
|
79
|
+
deviation: args.deviation ?? 0.5,
|
|
80
|
+
unit_scale: args.unit_scale ?? 0.1,
|
|
81
|
+
poker_dir: POKER_INSTALL_DIR.replace(/\\/g, '/'),
|
|
82
|
+
mu_energy: args.mu_energy ?? 1.25,
|
|
83
|
+
};
|
|
84
|
+
if (args.source_name) spec.source_name = args.source_name;
|
|
85
|
+
if (args.buildup_exclude) spec.buildup_exclude = args.buildup_exclude;
|
|
86
|
+
if (args.chunk) spec.chunk = args.chunk;
|
|
87
|
+
|
|
88
|
+
// カスタム材料のビルドアップ等価材料を自動で解決する。
|
|
89
|
+
// gen_paths.py は層の縮約先を決める際、単層ビルドアップデータの有無を
|
|
90
|
+
// 材料名で照合する。Source_Dry のようなカスタム材料は標準材料に
|
|
91
|
+
// 読み替える必要があり、従来は spec.equivalent に手で書いていた。
|
|
92
|
+
// MaterialCatalog が lib_equivalent.dat と実データから解決できるので
|
|
93
|
+
// ここで埋める(POKER 側も同じ表を見るので食い違わない)。
|
|
94
|
+
try {
|
|
95
|
+
const data = taskManager.data || {};
|
|
96
|
+
const equiv = {};
|
|
97
|
+
for (const z of (data.zone || [])) {
|
|
98
|
+
const m = z.material;
|
|
99
|
+
if (!m || m === 'VOID' || equiv[m]) continue;
|
|
100
|
+
if (MaterialCatalog.isStandard && MaterialCatalog.isStandard(m)) continue;
|
|
101
|
+
const e = MaterialCatalog.nearestBuildupEquivalent(m);
|
|
102
|
+
const name = (e && typeof e === 'object') ? e.equivalent : e;
|
|
103
|
+
if (name) equiv[m] = name;
|
|
104
|
+
}
|
|
105
|
+
if (Object.keys(equiv).length) {
|
|
106
|
+
spec.equivalent = equiv;
|
|
107
|
+
logger.info('ビルドアップ等価材料を自動設定しました', { equivalent: equiv });
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
logger.warn('等価材料の自動解決に失敗(spec.equivalent なしで続行)',
|
|
111
|
+
{ error: e.message });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const specFile = path.join(TASKS_DIR, '.generate_paths_spec.json');
|
|
115
|
+
await fs.writeFile(specFile, JSON.stringify(spec, null, 2), 'utf8');
|
|
116
|
+
|
|
117
|
+
// --- 3. FreeCAD をヘッドレスで起動してトレース ---
|
|
118
|
+
// GUI の MCP ブリッジ経由では、数万レイのトレースが GUI スレッドを
|
|
119
|
+
// 占有してブリッジが壊れる。freecadcmd を使う。
|
|
120
|
+
const script =
|
|
121
|
+
'import sys, json, traceback\n' +
|
|
122
|
+
'sys.path.insert(0, r"' + CAD_TOOLS_DIR + '")\n' +
|
|
123
|
+
'try:\n' +
|
|
124
|
+
' import gen_paths\n' +
|
|
125
|
+
' gen_paths.main(r"' + specFile + '")\n' +
|
|
126
|
+
' print("GEN_PATHS_OK")\n' +
|
|
127
|
+
'except BaseException:\n' +
|
|
128
|
+
' print("GEN_PATHS_ERROR")\n' +
|
|
129
|
+
' traceback.print_exc()\n';
|
|
130
|
+
|
|
131
|
+
const scriptFile = path.join(TASKS_DIR, '.generate_paths.py');
|
|
132
|
+
await fs.writeFile(scriptFile, script, 'utf8');
|
|
133
|
+
|
|
134
|
+
const r2 = await run(FREECAD_CMD, [scriptFile], { cwd: TASKS_DIR });
|
|
135
|
+
const log = (r2.out || '') + (r2.err || '');
|
|
136
|
+
|
|
137
|
+
if (!log.includes('GEN_PATHS_OK') || !fssync.existsSync(out)) {
|
|
138
|
+
// Python のトレースバックから要点を抜く
|
|
139
|
+
const m = log.match(/(?:SystemExit|Error|Exception)[^\n]*(?:\n[^\n]*){0,3}/);
|
|
140
|
+
return fail('経路の生成に失敗しました',
|
|
141
|
+
m ? m[0].trim() : log.slice(-500));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --- 4. 生成結果を要約する ---
|
|
145
|
+
const text = await fs.readFile(out, 'utf8');
|
|
146
|
+
const info = {};
|
|
147
|
+
for (const key of ['n_sources', 'n_source_points', 'n_detectors', 'n_materials',
|
|
148
|
+
'format_version', 'deviation_mm']) {
|
|
149
|
+
const m = text.match(new RegExp('^\\s*' + key + ':\\s*(.+)$', 'm'));
|
|
150
|
+
if (m) info[key] = m[1].trim();
|
|
151
|
+
}
|
|
152
|
+
const nPaths = (text.split('paths: |')[1] || '').trim().split('\n').length;
|
|
153
|
+
|
|
154
|
+
logger.info('経路ファイルを生成しました', { out, ...info });
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
content: [{
|
|
158
|
+
type: 'text',
|
|
159
|
+
text: JSON.stringify({
|
|
160
|
+
success: true,
|
|
161
|
+
message: '経路ファイルを生成しました',
|
|
162
|
+
output: out,
|
|
163
|
+
summary: {
|
|
164
|
+
経路数: nPaths,
|
|
165
|
+
線源数: info.n_sources,
|
|
166
|
+
線源点数: info.n_source_points,
|
|
167
|
+
検出器評価点数: info.n_detectors,
|
|
168
|
+
材質数: info.n_materials,
|
|
169
|
+
テッセレーション偏差_mm: info.deviation_mm,
|
|
170
|
+
format_version: info.format_version
|
|
171
|
+
},
|
|
172
|
+
spec_used: {
|
|
173
|
+
deviation: spec.deviation,
|
|
174
|
+
unit_scale: spec.unit_scale,
|
|
175
|
+
mu_energy: spec.mu_energy,
|
|
176
|
+
buildup_exclude: spec.buildup_exclude ?? ['VOID', 'Air'],
|
|
177
|
+
equivalent: spec.equivalent ?? {},
|
|
178
|
+
freecad: FREECAD_CMD,
|
|
179
|
+
poker_dir: spec.poker_dir
|
|
180
|
+
},
|
|
181
|
+
next: 'poker_cui "' + yamlFile + '" --path-input "' + out + '" -t で計算できます'
|
|
182
|
+
}, null, 2)
|
|
183
|
+
}]
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -6,7 +6,8 @@ import { createBuildupFactorHandlers } from './buildupFactorHandlers.js';
|
|
|
6
6
|
import { createSourceHandlers } from './sourceHandlers.js';
|
|
7
7
|
import { createDetectorHandlers } from './detectorHandlers.js';
|
|
8
8
|
import { createUnitHandlers } from './unitHandlers.js';
|
|
9
|
-
import { createThinnedIndicesHandlers } from './thinnedIndicesHandlers.js';
|
|
9
|
+
import { createThinnedIndicesHandlers } from './thinnedIndicesHandlers.js';
|
|
10
|
+
import { createCadHandlers } from './cadHandlers.js';
|
|
10
11
|
import { createCalculationHandlers } from './calculationHandlers.js';
|
|
11
12
|
import { resetHandlers } from './resetHandlers.js';
|
|
12
13
|
import { createDaughterNuclideHandler } from './daughterNuclideHandler.js';
|
|
@@ -34,7 +35,8 @@ export function createAllHandlers(taskManager) {
|
|
|
34
35
|
|
|
35
36
|
// 単位操作
|
|
36
37
|
...createUnitHandlers(taskManager),
|
|
37
|
-
...createThinnedIndicesHandlers(taskManager),
|
|
38
|
+
...createThinnedIndicesHandlers(taskManager),
|
|
39
|
+
...createCadHandlers(taskManager),
|
|
38
40
|
|
|
39
41
|
// 計算操作
|
|
40
42
|
...createCalculationHandlers(taskManager),
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// cadTools.js
|
|
2
|
+
//
|
|
3
|
+
// CAD 連携レイトレース(.paths の生成)のツール定義。
|
|
4
|
+
//
|
|
5
|
+
// FreeCAD のソリッドモデルから線源点→検出器の経路を抽出し、POKER に渡す
|
|
6
|
+
// .paths ファイルを作る。従来は freecadcmd を手で叩き、spec.json を手書きする
|
|
7
|
+
// 必要があったが、このツールがその手順をまとめる。
|
|
8
|
+
//
|
|
9
|
+
// FreeCAD の場所は環境変数 FREECAD_PATH で指定する。未設定なら既定の
|
|
10
|
+
// インストール先を探す。
|
|
11
|
+
|
|
12
|
+
export const cadTools = [
|
|
13
|
+
{
|
|
14
|
+
name: 'poker_generatePaths',
|
|
15
|
+
description:
|
|
16
|
+
'FreeCAD のモデルから経路ファイル(.paths)を生成します。線源分割点→検出器評価点の直線を追跡し、通過した材質と厚さを記録します。' +
|
|
17
|
+
'CSG プリミティブで表現しにくい形状(フィレット、自由曲面、多数の貫通孔など)をそのまま遮蔽計算に使えます。' +
|
|
18
|
+
'内部で poker_cui -p を実行して分割点と評価点を取得し、FreeCAD をヘッドレスで起動してトレースします。' +
|
|
19
|
+
'生成後は executeCalculation の path_input に指定するか、poker_cui --path-input で計算します。',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
fcstd: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'FreeCAD モデルのパス(.FCStd)。ソリッドに PokerMaterial プロパティで材質名を設定しておくこと。'
|
|
26
|
+
},
|
|
27
|
+
output: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: '出力する .paths のパス。省略時は作業ディレクトリの poker.paths。'
|
|
30
|
+
},
|
|
31
|
+
deviation: {
|
|
32
|
+
type: 'number',
|
|
33
|
+
minimum: 0.001,
|
|
34
|
+
description: 'テッセレーション偏差 [mm]。既定 0.5。曲面を三角形で近似する際の最大のずれ。球面や複曲面が支配的な体系では小さくする(0.1 で誤差が約 1/10 になる)。'
|
|
35
|
+
},
|
|
36
|
+
unit_scale: {
|
|
37
|
+
type: 'number',
|
|
38
|
+
description: 'CAD 単位 → POKER 単位の倍率。既定 0.1(FreeCAD の mm → POKER の cm)。'
|
|
39
|
+
},
|
|
40
|
+
mu_energy: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
description: '層の縮約に使う参照エネルギー [MeV]。既定 1.25。ビルドアップ層の選定(参考情報)にのみ影響し、線量計算には使われない。'
|
|
43
|
+
},
|
|
44
|
+
source_name: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: '線源名。省略時は全線源を対象にする。'
|
|
47
|
+
},
|
|
48
|
+
buildup_exclude: {
|
|
49
|
+
type: 'array',
|
|
50
|
+
items: { type: 'string' },
|
|
51
|
+
description: 'ビルドアップ層の候補から除外する材質。既定 ["VOID","Air"]。大気中の長距離伝播で Air を遮蔽として数えたい場合などに変更する。減衰計算には影響せず、層の縮約(参考情報)にのみ効く。'
|
|
52
|
+
},
|
|
53
|
+
chunk: {
|
|
54
|
+
type: 'integer',
|
|
55
|
+
minimum: 1024,
|
|
56
|
+
description: 'レイトレースのバッチサイズ。既定 32768。メモリが厳しい環境では下げる。結果には影響しない。'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
required: ['fcstd'],
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
title: 'CAD から経路ファイルを生成',
|
|
62
|
+
description: '入力 YAML の線源・検出器の定義と、FreeCAD のモデルを突き合わせて .paths を作ります。'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
];
|
|
@@ -17,7 +17,7 @@ export const calculationTools = [
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
name: 'poker_executeCalculation',
|
|
20
|
-
description: '作成したYAMLファイルを使用してpoker_cui
|
|
20
|
+
description: '作成したYAMLファイルを使用してpoker_cuiで放射線遮蔽計算を実行します。path_input に .paths を指定すると、CAD から抽出した経路を幾何として使います。応答には .summary(YAML) から抽出した構造化 result_total(検出器ごとの座標と E(AP)/DskinM(AP)/H*(10) の線量内訳)、dose_columns、警告フッタ calculation_warnings、注記 calculation_notes を含みます。',
|
|
21
21
|
inputSchema: {
|
|
22
22
|
type: 'object',
|
|
23
23
|
properties: {
|
|
@@ -26,6 +26,10 @@ export const calculationTools = [
|
|
|
26
26
|
description: '計算に使用するYAMLファイル名(拡張子.yamlを含む)。ファイル名のみ指定(例: poker.yaml)で、POKER_MCP_HOME/tasks/ 配下のファイルが自動的に参照されます。絶対パスも指定可能です。',
|
|
27
27
|
pattern: '^([a-zA-Z0-9_\\-\\.]+\\.(yaml|yml)|([a-zA-Z]:[\\\\\/]|\\/)[^\\\\]+\\.(yaml|yml))$'
|
|
28
28
|
},
|
|
29
|
+
path_input: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'CAD から抽出した経路ファイル(.paths)。指定すると、YAML の立体・ゾーン定義の代わりにこのファイルの経路を幾何として使います(poker_cui の --path-input)。poker_generatePaths で生成できます。線源・検出器・材料・ビルドアップ設定は従来どおり YAML から取得するので、入力の正本は YAML に保たれます。ファイル名のみの指定で POKER_MCP_HOME/tasks/ 配下が参照されます。'
|
|
32
|
+
},
|
|
29
33
|
summary_options: {
|
|
30
34
|
type: 'object',
|
|
31
35
|
description: 'サマリー出力オプション(注意: show_source_dataとshow_total_doseの少なくとも一方は必須です)',
|
package/src/mcp/tools/index.js
CHANGED
|
@@ -7,7 +7,8 @@ import { sourceTools } from './sourceTools.js';
|
|
|
7
7
|
import { detectorTools } from './detectorTools.js';
|
|
8
8
|
import { commonTools } from './commonTools.js';
|
|
9
9
|
import { unitTools } from './unitTools.js';
|
|
10
|
-
import { thinnedIndicesTools } from './thinnedIndicesTools.js';
|
|
10
|
+
import { thinnedIndicesTools } from './thinnedIndicesTools.js';
|
|
11
|
+
import { cadTools } from './cadTools.js';
|
|
11
12
|
import { calculationTools } from './calculationTools.js';
|
|
12
13
|
import { resetTools } from './resetTools.js';
|
|
13
14
|
import { daughterNuclideTools } from './daughterNuclideTools.js';
|
|
@@ -22,7 +23,8 @@ export const allTools = [
|
|
|
22
23
|
...detectorTools,
|
|
23
24
|
...commonTools,
|
|
24
25
|
...unitTools,
|
|
25
|
-
...thinnedIndicesTools,
|
|
26
|
+
...thinnedIndicesTools,
|
|
27
|
+
...cadTools,
|
|
26
28
|
...calculationTools,
|
|
27
29
|
...resetTools,
|
|
28
30
|
...daughterNuclideTools,
|
|
@@ -38,7 +40,8 @@ export {
|
|
|
38
40
|
detectorTools,
|
|
39
41
|
commonTools,
|
|
40
42
|
unitTools,
|
|
41
|
-
thinnedIndicesTools,
|
|
43
|
+
thinnedIndicesTools,
|
|
44
|
+
cadTools,
|
|
42
45
|
calculationTools,
|
|
43
46
|
resetTools,
|
|
44
47
|
daughterNuclideTools,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// services/CalculationService.js
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
import { promises as fs } from 'fs';
|
|
4
|
-
import path from 'path';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { TASKS_DIR } from '../utils/paths.js';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
6
7
|
import { CalculationError } from '../utils/errors.js';
|
|
7
8
|
|
|
@@ -240,7 +241,7 @@ export class CalculationService {
|
|
|
240
241
|
* @param {number} timeout タイムアウト時間(ミリ秒)
|
|
241
242
|
* @returns {Promise<Object>} 計算結果
|
|
242
243
|
*/
|
|
243
|
-
async executeCalculation(yamlFile, summaryOptions = {}, outputFiles = {}, timeout = this.defaultTimeout) {
|
|
244
|
+
async executeCalculation(yamlFile, summaryOptions = {}, outputFiles = {}, timeout = this.defaultTimeout, pathInput = null) {
|
|
244
245
|
// 前処理: 各種検証
|
|
245
246
|
logger.info('Starting calculation validation', { yamlFile, summaryOptions, outputFiles });
|
|
246
247
|
|
|
@@ -301,7 +302,7 @@ export class CalculationService {
|
|
|
301
302
|
absoluteYamlPath = yamlFile;
|
|
302
303
|
} else {
|
|
303
304
|
// 相対パスの場合、tasks/ディレクトリからの相対パスとして解決
|
|
304
|
-
absoluteYamlPath = path.
|
|
305
|
+
absoluteYamlPath = path.join(TASKS_DIR, yamlFile);
|
|
305
306
|
}
|
|
306
307
|
const args = [absoluteYamlPath];
|
|
307
308
|
|
|
@@ -318,6 +319,16 @@ export class CalculationService {
|
|
|
318
319
|
args.push('-d', outputFiles.dose_file);
|
|
319
320
|
}
|
|
320
321
|
|
|
322
|
+
// CAD から抽出した経路を幾何として使う。
|
|
323
|
+
// YAML の立体・ゾーン定義の代わりに .paths の経路を読む。線源・検出器・
|
|
324
|
+
// 材料・ビルドアップ設定は従来どおり YAML から取得するので、入力の
|
|
325
|
+
// 正本は YAML に保たれる。
|
|
326
|
+
if (pathInput) {
|
|
327
|
+
const absPathInput = path.isAbsolute(pathInput)
|
|
328
|
+
? pathInput : path.join(TASKS_DIR, pathInput);
|
|
329
|
+
args.push('--path-input', absPathInput);
|
|
330
|
+
}
|
|
331
|
+
|
|
321
332
|
logger.info('Executing poker_cui calculation', {
|
|
322
333
|
command: this.pokercuiCommand,
|
|
323
334
|
args,
|
|
@@ -540,7 +551,7 @@ export class CalculationService {
|
|
|
540
551
|
* @param {Object} dataManager - データマネージャーインスタンス(オプション)
|
|
541
552
|
* @returns {Promise<Object>} 実行結果
|
|
542
553
|
*/
|
|
543
|
-
async executeWithValidation(yamlFile, summaryOptions = {}, outputFiles = {}, timeout = this.defaultTimeout, dataManager = null) {
|
|
554
|
+
async executeWithValidation(yamlFile, summaryOptions = {}, outputFiles = {}, timeout = this.defaultTimeout, dataManager = null, pathInput = null) {
|
|
544
555
|
try {
|
|
545
556
|
logger.info('統合検証付き計算実行を開始', { yamlFile });
|
|
546
557
|
|
|
@@ -615,7 +626,7 @@ export class CalculationService {
|
|
|
615
626
|
}
|
|
616
627
|
|
|
617
628
|
// 通常の計算実行
|
|
618
|
-
const calculationResult = await this.executeCalculation(yamlFile, summaryOptions, outputFiles, timeout);
|
|
629
|
+
const calculationResult = await this.executeCalculation(yamlFile, summaryOptions, outputFiles, timeout, pathInput);
|
|
619
630
|
|
|
620
631
|
// poker_cuiがエラーを返した場合、事後診断を実行
|
|
621
632
|
if (!calculationResult.success && dataManager) {
|
package/src/utils/paths.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// "POKER_INSTALL_PATH": "C:\\Poker"
|
|
11
11
|
// }
|
|
12
12
|
//
|
|
13
|
+
import fs from 'fs';
|
|
13
14
|
import path from 'path';
|
|
14
15
|
import os from 'os';
|
|
15
16
|
|
|
@@ -24,6 +25,88 @@ export const BACKUPS_DIR = path.join(BASE_DIR, 'backups');
|
|
|
24
25
|
export const POKER_INSTALL_DIR = process.env.POKER_INSTALL_PATH ?? 'C:/Poker';
|
|
25
26
|
export const POKER_LIB_DIR = path.join(POKER_INSTALL_DIR, 'LIB');
|
|
26
27
|
|
|
28
|
+
// FreeCAD のコマンドライン版(freecadcmd)。CAD 連携レイトレースで使う。
|
|
29
|
+
//
|
|
30
|
+
// 環境変数 FREECAD_PATH で指定する。実行ファイルそのものでも、
|
|
31
|
+
// インストールフォルダ(bin の親)でも受け付ける。
|
|
32
|
+
//
|
|
33
|
+
// 未設定なら既定の場所を順に探す。見つからなければ null を返し、
|
|
34
|
+
// CAD 連携のツールを呼んだときに「設定してください」と案内する。
|
|
35
|
+
// POKER_INSTALL_PATH のように既定値を決め打ちしないのは、FreeCAD の
|
|
36
|
+
// インストール先がバージョン番号を含み(FreeCAD 1.1, FreeCAD 1.0 …)
|
|
37
|
+
// 決め打ちが当たらないため。
|
|
38
|
+
// 探索の順序:
|
|
39
|
+
// 1. 環境変数 FREECAD_PATH(実行ファイルでもインストールフォルダでも可)
|
|
40
|
+
// 2. PATH に freecadcmd があるか
|
|
41
|
+
// 3. 既定のインストール先を走査(バージョン番号を決め打ちしない)
|
|
42
|
+
//
|
|
43
|
+
// バージョン番号を含むフォルダ(FreeCAD 1.1, FreeCAD 1.0 …)に入るため、
|
|
44
|
+
// POKER_INSTALL_PATH のような固定の既定値は使えない。
|
|
45
|
+
const FREECAD_SEARCH_ROOTS = [
|
|
46
|
+
'C:/Program Files',
|
|
47
|
+
'C:/Program Files (x86)',
|
|
48
|
+
'/usr/lib',
|
|
49
|
+
'/opt',
|
|
50
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs') : null,
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
|
|
53
|
+
const FREECAD_EXE = process.platform === 'win32' ? 'freecadcmd.exe' : 'freecadcmd';
|
|
54
|
+
|
|
55
|
+
function underDir(dir) {
|
|
56
|
+
for (const rel of [path.join('bin', FREECAD_EXE), FREECAD_EXE]) {
|
|
57
|
+
const c = path.join(dir, rel);
|
|
58
|
+
if (fs.existsSync(c)) return c;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fromPathEnv() {
|
|
64
|
+
const sep = process.platform === 'win32' ? ';' : ':';
|
|
65
|
+
for (const d of (process.env.PATH || '').split(sep)) {
|
|
66
|
+
if (!d) continue;
|
|
67
|
+
const c = path.join(d, FREECAD_EXE);
|
|
68
|
+
try { if (fs.existsSync(c)) return c; } catch { /* 権限などは無視 */ }
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scanRoots() {
|
|
74
|
+
// FreeCAD* に一致するフォルダを探す。新しいバージョンから試すため降順。
|
|
75
|
+
for (const root of FREECAD_SEARCH_ROOTS) {
|
|
76
|
+
let entries;
|
|
77
|
+
try { entries = fs.readdirSync(root); } catch { continue; }
|
|
78
|
+
const cands = entries.filter(n => /^freecad/i.test(n)).sort().reverse();
|
|
79
|
+
for (const n of cands) {
|
|
80
|
+
const hit = underDir(path.join(root, n));
|
|
81
|
+
if (hit) return hit;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Linux の標準的な配置
|
|
85
|
+
for (const c of ['/usr/bin/freecadcmd', '/usr/local/bin/freecadcmd']) {
|
|
86
|
+
if (fs.existsSync(c)) return c;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolveFreeCad() {
|
|
92
|
+
const env = process.env.FREECAD_PATH;
|
|
93
|
+
if (env) {
|
|
94
|
+
if (fs.existsSync(env) && fs.statSync(env).isFile()) return env;
|
|
95
|
+
const hit = underDir(env);
|
|
96
|
+
if (hit) return hit;
|
|
97
|
+
return null; // 指定されているが見つからない(呼び出し側で案内する)
|
|
98
|
+
}
|
|
99
|
+
return fromPathEnv() || scanRoots();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const FREECAD_CMD = resolveFreeCad();
|
|
103
|
+
|
|
104
|
+
// CAD 連携ツール(tools/ 配下の Python)の場所。
|
|
105
|
+
// npm パッケージにも同梱するので、このファイルからの相対で解決する。
|
|
106
|
+
export const CAD_TOOLS_DIR = path.resolve(
|
|
107
|
+
path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1')),
|
|
108
|
+
'..', '..', 'tools');
|
|
109
|
+
|
|
27
110
|
export const YAML_FILE = path.join(TASKS_DIR, 'poker.yaml');
|
|
28
111
|
export const PENDING_FILE = path.join(TASKS_DIR, 'pending_changes.json');
|
|
29
112
|
export const USER_CONFIG = path.join(BASE_DIR, 'config.json');
|