poker-mcp 1.0.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.md +241 -0
- package/config/mcp-manifest.json +1713 -0
- package/config/package-lock.json +1335 -0
- package/config/poker_mcp.code-workspace +8 -0
- package/package.json +51 -0
- package/src/config/ConfigManager.js +120 -0
- package/src/config/default.json +62 -0
- package/src/mcp/handlers/bodyHandlers.js +63 -0
- package/src/mcp/handlers/buildupFactorHandlers.js +43 -0
- package/src/mcp/handlers/calculationHandlers.js +265 -0
- package/src/mcp/handlers/detectorHandlers.js +201 -0
- package/src/mcp/handlers/index.js +43 -0
- package/src/mcp/handlers/sourceHandlers.js +25 -0
- package/src/mcp/handlers/transformHandlers.js +26 -0
- package/src/mcp/handlers/unitHandlers.js +305 -0
- package/src/mcp/handlers/zoneHandlers.js +83 -0
- package/src/mcp/middleware/errorHandler.js +49 -0
- package/src/mcp/middleware/requestValidator.js +129 -0
- package/src/mcp/server.js +76 -0
- package/src/mcp/tools/bodyTools.js +185 -0
- package/src/mcp/tools/buildupFactorTools.js +82 -0
- package/src/mcp/tools/calculationTools.js +58 -0
- package/src/mcp/tools/commonTools.js +22 -0
- package/src/mcp/tools/detectorTools.js +131 -0
- package/src/mcp/tools/index.js +34 -0
- package/src/mcp/tools/sourceTools.js +726 -0
- package/src/mcp/tools/sourceTools_fixed_part2.js +41 -0
- package/src/mcp/tools/transformTools.js +73 -0
- package/src/mcp/tools/unitTools.js +137 -0
- package/src/mcp/tools/zoneTools.js +65 -0
- package/src/mcp_server_stdio_v4.js +45 -0
- package/src/routes/restApi.js +184 -0
- package/src/services/CalculationService.js +525 -0
- package/src/services/DataManager.js +738 -0
- package/src/services/TaskManager.js +1622 -0
- package/src/utils/MaterialAlternatives.js +189 -0
- package/src/utils/errorHandler.js +68 -0
- package/src/utils/errors.js +37 -0
- package/src/utils/logger.js +24 -0
- package/src/utils/logger_no_color.js +28 -0
- package/src/utils/mcpErrors.js +225 -0
- package/src/validators/CMBValidator.js +191 -0
- package/src/validators/DetectorValidator.js +512 -0
- package/src/validators/ManifestValidator.js +249 -0
- package/src/validators/NuclideValidator.js +171 -0
- package/src/validators/PhysicsValidator.js +401 -0
- package/src/validators/PhysicsValidatorSimple.js +87 -0
- package/src/validators/SourceValidator.js +515 -0
- package/src/validators/TransformValidator.js +472 -0
- package/src/validators/UnitValidator.js +562 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
// validators/UnitValidator.js
|
|
2
|
+
import { PokerMcpError } from '../utils/mcpErrors.js';
|
|
3
|
+
import { ManifestValidator } from './ManifestValidator.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Unit系の4キー完全性保証バリデーター
|
|
7
|
+
* length, angle, density, radioactivityの4キー必須性を厳格管理
|
|
8
|
+
*/
|
|
9
|
+
export class UnitValidator {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 必須4キーの完全定義(遅延初期化版)
|
|
13
|
+
* マニフェスト仕様に完全準拠
|
|
14
|
+
*/
|
|
15
|
+
static get REQUIRED_UNIT_KEYS() {
|
|
16
|
+
if (!this._cachedUnitKeys) {
|
|
17
|
+
this._cachedUnitKeys = {
|
|
18
|
+
length: {
|
|
19
|
+
description: '長さの単位',
|
|
20
|
+
allowedValues: ['m', 'cm', 'mm'],
|
|
21
|
+
default: 'cm',
|
|
22
|
+
physicalDimension: 'L',
|
|
23
|
+
siBase: 'm',
|
|
24
|
+
conversionFactors: {
|
|
25
|
+
'm': 1.0,
|
|
26
|
+
'cm': 0.01,
|
|
27
|
+
'mm': 0.001
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
angle: {
|
|
31
|
+
description: '角度の単位',
|
|
32
|
+
allowedValues: ['radian', 'degree'],
|
|
33
|
+
default: 'radian',
|
|
34
|
+
physicalDimension: 'dimensionless',
|
|
35
|
+
siBase: 'radian',
|
|
36
|
+
conversionFactors: {
|
|
37
|
+
'radian': 1.0,
|
|
38
|
+
'degree': Math.PI / 180.0 // 遅延計算
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
density: {
|
|
42
|
+
description: '密度の単位',
|
|
43
|
+
allowedValues: ['g/cm3'],
|
|
44
|
+
default: 'g/cm3',
|
|
45
|
+
physicalDimension: 'ML^-3',
|
|
46
|
+
siBase: 'kg/m3',
|
|
47
|
+
conversionFactors: {
|
|
48
|
+
'g/cm3': 1000.0 // g/cm³ to kg/m³
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
radioactivity: {
|
|
52
|
+
description: '放射能の単位',
|
|
53
|
+
allowedValues: ['Bq'],
|
|
54
|
+
default: 'Bq',
|
|
55
|
+
physicalDimension: 'T^-1',
|
|
56
|
+
siBase: 'Bq',
|
|
57
|
+
conversionFactors: {
|
|
58
|
+
'Bq': 1.0
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return this._cachedUnitKeys;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 単位システムの物理的整合性チェック用定数
|
|
68
|
+
*/
|
|
69
|
+
static PHYSICAL_CONSISTENCY = {
|
|
70
|
+
dimensionalAnalysis: {
|
|
71
|
+
'L': 'length', // 長さ次元
|
|
72
|
+
'T': 'time', // 時間次元
|
|
73
|
+
'M': 'mass', // 質量次元
|
|
74
|
+
'ML^-3': 'density', // 密度次元
|
|
75
|
+
'T^-1': 'frequency' // 頻度次元(放射能)
|
|
76
|
+
},
|
|
77
|
+
unitCompatibility: {
|
|
78
|
+
volume: ['length^3'],
|
|
79
|
+
area: ['length^2'],
|
|
80
|
+
mass: ['density * volume'],
|
|
81
|
+
activity: ['radioactivity']
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 4キー完全性の基本検証
|
|
87
|
+
* 全ての必須キーの存在と型を確認
|
|
88
|
+
*/
|
|
89
|
+
static validateFourKeyCompleteness(unitData) {
|
|
90
|
+
if (!unitData || typeof unitData !== 'object') {
|
|
91
|
+
throw PokerMcpError.validationError(
|
|
92
|
+
'Unit data must be an object with all 4 required keys',
|
|
93
|
+
'unitData',
|
|
94
|
+
unitData
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const providedKeys = Object.keys(unitData);
|
|
99
|
+
const requiredKeys = Object.keys(this.REQUIRED_UNIT_KEYS);
|
|
100
|
+
|
|
101
|
+
// 必須キーの存在チェック
|
|
102
|
+
const missingKeys = requiredKeys.filter(key => !providedKeys.includes(key));
|
|
103
|
+
if (missingKeys.length > 0) {
|
|
104
|
+
throw PokerMcpError.validationError(
|
|
105
|
+
`Missing required unit keys: ${missingKeys.join(', ')}. All 4 keys (length, angle, density, radioactivity) are mandatory`,
|
|
106
|
+
'missingKeys',
|
|
107
|
+
missingKeys
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 余分なキーの検出
|
|
112
|
+
const extraKeys = providedKeys.filter(key => !requiredKeys.includes(key));
|
|
113
|
+
if (extraKeys.length > 0) {
|
|
114
|
+
throw PokerMcpError.validationError(
|
|
115
|
+
`Unknown unit keys found: ${extraKeys.join(', ')}. Only the 4 required keys are allowed`,
|
|
116
|
+
'extraKeys',
|
|
117
|
+
extraKeys
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 各キーの値検証
|
|
122
|
+
for (const [key, value] of Object.entries(unitData)) {
|
|
123
|
+
this.validateSingleUnitKey(key, value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 単一単位キーの詳細検証
|
|
131
|
+
*/
|
|
132
|
+
static validateSingleUnitKey(key, value) {
|
|
133
|
+
const keySpec = this.REQUIRED_UNIT_KEYS[key];
|
|
134
|
+
|
|
135
|
+
if (!keySpec) {
|
|
136
|
+
throw PokerMcpError.validationError(
|
|
137
|
+
`Invalid unit key: ${key}`,
|
|
138
|
+
'key',
|
|
139
|
+
key
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 値の型チェック
|
|
144
|
+
if (typeof value !== 'string') {
|
|
145
|
+
throw PokerMcpError.validationError(
|
|
146
|
+
`Unit key '${key}' value must be a string`,
|
|
147
|
+
key,
|
|
148
|
+
value
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 値の空文字チェック
|
|
153
|
+
if (!value.trim()) {
|
|
154
|
+
throw PokerMcpError.validationError(
|
|
155
|
+
`Unit key '${key}' cannot be empty`,
|
|
156
|
+
key,
|
|
157
|
+
value
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 許可値チェック
|
|
162
|
+
if (!keySpec.allowedValues.includes(value)) {
|
|
163
|
+
throw PokerMcpError.validationError(
|
|
164
|
+
`Invalid value '${value}' for unit key '${key}'. Allowed values: ${keySpec.allowedValues.join(', ')}`,
|
|
165
|
+
key,
|
|
166
|
+
value,
|
|
167
|
+
`Expected one of: ${keySpec.allowedValues.join(', ')}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Unit構造の物理的整合性検証
|
|
176
|
+
* 単位系の物理的な一貫性をチェック
|
|
177
|
+
*/
|
|
178
|
+
static validatePhysicalConsistency(unitData) {
|
|
179
|
+
this.validateFourKeyCompleteness(unitData);
|
|
180
|
+
|
|
181
|
+
const consistencyReport = {
|
|
182
|
+
isConsistent: true,
|
|
183
|
+
warnings: [],
|
|
184
|
+
recommendations: []
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// SI単位系との整合性チェック
|
|
188
|
+
for (const [key, value] of Object.entries(unitData)) {
|
|
189
|
+
const keySpec = this.REQUIRED_UNIT_KEYS[key];
|
|
190
|
+
const conversionFactor = keySpec.conversionFactors[value];
|
|
191
|
+
|
|
192
|
+
if (Math.abs(conversionFactor - 1.0) > 1e-10) {
|
|
193
|
+
consistencyReport.warnings.push({
|
|
194
|
+
type: 'non_si_unit',
|
|
195
|
+
key,
|
|
196
|
+
value,
|
|
197
|
+
message: `Using non-SI unit '${value}' for ${key}. SI base unit is '${keySpec.siBase}'`,
|
|
198
|
+
conversionFactor
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 単位系の組み合わせ整合性
|
|
204
|
+
const lengthUnit = unitData.length;
|
|
205
|
+
const densityUnit = unitData.density;
|
|
206
|
+
|
|
207
|
+
// 密度と長さの単位の組み合わせチェック
|
|
208
|
+
if (lengthUnit === 'cm' && densityUnit === 'g/cm3') {
|
|
209
|
+
// CGS系の組み合わせ - 良好
|
|
210
|
+
consistencyReport.recommendations.push({
|
|
211
|
+
type: 'unit_system_consistency',
|
|
212
|
+
message: 'Using consistent CGS unit system (cm + g/cm³)'
|
|
213
|
+
});
|
|
214
|
+
} else if (lengthUnit === 'm' && densityUnit === 'g/cm3') {
|
|
215
|
+
// 混合系 - 注意喚起
|
|
216
|
+
consistencyReport.warnings.push({
|
|
217
|
+
type: 'mixed_unit_system',
|
|
218
|
+
message: 'Mixing SI length (m) with CGS density (g/cm³). Consider using consistent unit system'
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 角度単位の使用状況チェック
|
|
223
|
+
const angleUnit = unitData.angle;
|
|
224
|
+
if (angleUnit === 'degree') {
|
|
225
|
+
consistencyReport.warnings.push({
|
|
226
|
+
type: 'angle_unit_warning',
|
|
227
|
+
message: 'Using degree for angles. Radian is preferred for scientific calculations'
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return consistencyReport;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Unit更新時の4キー保持検証
|
|
236
|
+
* 部分更新でも4キー構造の維持を保証
|
|
237
|
+
*/
|
|
238
|
+
static validatePartialUpdate(currentUnits, updates) {
|
|
239
|
+
if (!currentUnits) {
|
|
240
|
+
throw PokerMcpError.validationError(
|
|
241
|
+
'Current unit data is required for partial update validation',
|
|
242
|
+
'currentUnits',
|
|
243
|
+
currentUnits
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 現在の単位データの完全性を事前確認
|
|
248
|
+
this.validateFourKeyCompleteness(currentUnits);
|
|
249
|
+
|
|
250
|
+
if (!updates || typeof updates !== 'object') {
|
|
251
|
+
throw PokerMcpError.validationError(
|
|
252
|
+
'Update data must be an object',
|
|
253
|
+
'updates',
|
|
254
|
+
updates
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const updateKeys = Object.keys(updates);
|
|
259
|
+
|
|
260
|
+
// 更新キーの妥当性チェック
|
|
261
|
+
const invalidKeys = updateKeys.filter(key =>
|
|
262
|
+
!Object.keys(this.REQUIRED_UNIT_KEYS).includes(key)
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
if (invalidKeys.length > 0) {
|
|
266
|
+
throw PokerMcpError.validationError(
|
|
267
|
+
`Invalid unit keys in update: ${invalidKeys.join(', ')}`,
|
|
268
|
+
'invalidKeys',
|
|
269
|
+
invalidKeys
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 更新後の仮想データを構築して検証
|
|
274
|
+
const updatedUnits = { ...currentUnits, ...updates };
|
|
275
|
+
this.validateFourKeyCompleteness(updatedUnits);
|
|
276
|
+
|
|
277
|
+
// 更新される各キーの個別検証
|
|
278
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
279
|
+
this.validateSingleUnitKey(key, value);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
isValid: true,
|
|
284
|
+
updatedStructure: updatedUnits,
|
|
285
|
+
changedKeys: updateKeys,
|
|
286
|
+
preservedIntegrity: true
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Unit構造の正規化処理
|
|
292
|
+
* 4キー完全性を保持しつつデータを正規化
|
|
293
|
+
*/
|
|
294
|
+
static normalizeUnitStructure(unitData) {
|
|
295
|
+
// 基本検証
|
|
296
|
+
this.validateFourKeyCompleteness(unitData);
|
|
297
|
+
|
|
298
|
+
const normalized = {};
|
|
299
|
+
|
|
300
|
+
// 必須4キーを順序保証で処理
|
|
301
|
+
const keyOrder = ['length', 'angle', 'density', 'radioactivity'];
|
|
302
|
+
|
|
303
|
+
for (const key of keyOrder) {
|
|
304
|
+
const value = unitData[key];
|
|
305
|
+
const keySpec = this.REQUIRED_UNIT_KEYS[key];
|
|
306
|
+
|
|
307
|
+
// 値の正規化(空白除去、小文字化)
|
|
308
|
+
let normalizedValue = value.trim();
|
|
309
|
+
|
|
310
|
+
// 特別な正規化ルール
|
|
311
|
+
if (key === 'angle') {
|
|
312
|
+
if (normalizedValue === 'deg') normalizedValue = 'degree';
|
|
313
|
+
if (normalizedValue === 'rad') normalizedValue = 'radian';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 最終検証
|
|
317
|
+
this.validateSingleUnitKey(key, normalizedValue);
|
|
318
|
+
|
|
319
|
+
normalized[key] = normalizedValue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return normalized;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Unit変換係数の計算
|
|
327
|
+
* 異なる単位系間の変換係数を提供
|
|
328
|
+
*/
|
|
329
|
+
static calculateConversionFactors(fromUnits, toUnits) {
|
|
330
|
+
this.validateFourKeyCompleteness(fromUnits);
|
|
331
|
+
this.validateFourKeyCompleteness(toUnits);
|
|
332
|
+
|
|
333
|
+
const conversionFactors = {};
|
|
334
|
+
|
|
335
|
+
for (const key of Object.keys(this.REQUIRED_UNIT_KEYS)) {
|
|
336
|
+
const fromValue = fromUnits[key];
|
|
337
|
+
const toValue = toUnits[key];
|
|
338
|
+
const keySpec = this.REQUIRED_UNIT_KEYS[key];
|
|
339
|
+
|
|
340
|
+
const fromFactor = keySpec.conversionFactors[fromValue];
|
|
341
|
+
const toFactor = keySpec.conversionFactors[toValue];
|
|
342
|
+
|
|
343
|
+
// SI基準での変換係数計算
|
|
344
|
+
conversionFactors[key] = fromFactor / toFactor;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
factors: conversionFactors,
|
|
349
|
+
fromUnits,
|
|
350
|
+
toUnits,
|
|
351
|
+
isIdentity: Object.values(conversionFactors).every(f => Math.abs(f - 1.0) < 1e-10)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Unit系のシステム全体整合性検証
|
|
357
|
+
* YAMLファイル内でのUnit使用状況を包括チェック
|
|
358
|
+
*/
|
|
359
|
+
static validateSystemUnitIntegrity(unitData, yamlData) {
|
|
360
|
+
this.validateFourKeyCompleteness(unitData);
|
|
361
|
+
|
|
362
|
+
const integrityReport = {
|
|
363
|
+
unitStructure: 'valid',
|
|
364
|
+
usageConsistency: [],
|
|
365
|
+
potentialIssues: [],
|
|
366
|
+
recommendations: []
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// 長さ単位の使用状況チェック
|
|
370
|
+
const lengthUnit = unitData.length;
|
|
371
|
+
const lengthUsages = this.findLengthUsagesInYaml(yamlData);
|
|
372
|
+
|
|
373
|
+
if (lengthUsages.length > 0) {
|
|
374
|
+
integrityReport.usageConsistency.push({
|
|
375
|
+
unit: lengthUnit,
|
|
376
|
+
usageCount: lengthUsages.length,
|
|
377
|
+
contexts: lengthUsages.map(u => u.context)
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// 密度単位の使用状況チェック
|
|
382
|
+
const densityUnit = unitData.density;
|
|
383
|
+
const densityUsages = this.findDensityUsagesInYaml(yamlData);
|
|
384
|
+
|
|
385
|
+
if (densityUsages.length > 0) {
|
|
386
|
+
integrityReport.usageConsistency.push({
|
|
387
|
+
unit: densityUnit,
|
|
388
|
+
usageCount: densityUsages.length,
|
|
389
|
+
contexts: densityUsages.map(u => u.context)
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 放射能単位の使用状況チェック
|
|
394
|
+
const radioactivityUnit = unitData.radioactivity;
|
|
395
|
+
const radioactivityUsages = this.findRadioactivityUsagesInYaml(yamlData);
|
|
396
|
+
|
|
397
|
+
if (radioactivityUsages.length > 0) {
|
|
398
|
+
integrityReport.usageConsistency.push({
|
|
399
|
+
unit: radioactivityUnit,
|
|
400
|
+
usageCount: radioactivityUsages.length,
|
|
401
|
+
contexts: radioactivityUsages.map(u => u.context)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// 角度単位の使用状況チェック(Transform)
|
|
406
|
+
if (yamlData.transform && yamlData.transform.length > 0) {
|
|
407
|
+
integrityReport.usageConsistency.push({
|
|
408
|
+
unit: unitData.angle,
|
|
409
|
+
usageCount: yamlData.transform.length,
|
|
410
|
+
contexts: ['transform_rotations']
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return integrityReport;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* YAML内の長さ使用箇所を検索
|
|
419
|
+
* @private
|
|
420
|
+
*/
|
|
421
|
+
static findLengthUsagesInYaml(yamlData) {
|
|
422
|
+
const usages = [];
|
|
423
|
+
|
|
424
|
+
// Body座標での使用
|
|
425
|
+
if (yamlData.body) {
|
|
426
|
+
yamlData.body.forEach(body => {
|
|
427
|
+
usages.push({ context: `body.${body.name}`, type: 'coordinates' });
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Detector座標での使用
|
|
432
|
+
if (yamlData.detector) {
|
|
433
|
+
yamlData.detector.forEach(detector => {
|
|
434
|
+
usages.push({ context: `detector.${detector.name}`, type: 'coordinates' });
|
|
435
|
+
if (detector.grid) {
|
|
436
|
+
detector.grid.forEach((_, idx) => {
|
|
437
|
+
usages.push({ context: `detector.${detector.name}.grid[${idx}]`, type: 'edge_vector' });
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Source位置での使用
|
|
444
|
+
if (yamlData.source) {
|
|
445
|
+
yamlData.source.forEach(source => {
|
|
446
|
+
usages.push({ context: `source.${source.name}`, type: 'position' });
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return usages;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* YAML内の密度使用箇所を検索
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
static findDensityUsagesInYaml(yamlData) {
|
|
458
|
+
const usages = [];
|
|
459
|
+
|
|
460
|
+
if (yamlData.zone) {
|
|
461
|
+
yamlData.zone.forEach(zone => {
|
|
462
|
+
if (zone.density !== undefined) {
|
|
463
|
+
usages.push({ context: `zone.${zone.body_name}`, type: 'material_density' });
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return usages;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* YAML内の放射能使用箇所を検索
|
|
473
|
+
* @private
|
|
474
|
+
*/
|
|
475
|
+
static findRadioactivityUsagesInYaml(yamlData) {
|
|
476
|
+
const usages = [];
|
|
477
|
+
|
|
478
|
+
if (yamlData.source) {
|
|
479
|
+
yamlData.source.forEach(source => {
|
|
480
|
+
if (source.inventory) {
|
|
481
|
+
source.inventory.forEach((nuclide, idx) => {
|
|
482
|
+
usages.push({
|
|
483
|
+
context: `source.${source.name}.inventory[${idx}]`,
|
|
484
|
+
type: 'radioactivity',
|
|
485
|
+
nuclide: nuclide.nuclide
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return usages;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Unit系の完全性診断レポート
|
|
497
|
+
* 4キー構造の健全性を包括的に診断
|
|
498
|
+
*/
|
|
499
|
+
static generateIntegrityDiagnosticReport(unitData, yamlData = null) {
|
|
500
|
+
const report = {
|
|
501
|
+
timestamp: new Date().toISOString(),
|
|
502
|
+
unitStructure: null,
|
|
503
|
+
physicalConsistency: null,
|
|
504
|
+
systemIntegrity: null,
|
|
505
|
+
overallHealth: 'unknown',
|
|
506
|
+
criticalIssues: [],
|
|
507
|
+
warnings: [],
|
|
508
|
+
recommendations: []
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
try {
|
|
512
|
+
// 4キー完全性検証
|
|
513
|
+
this.validateFourKeyCompleteness(unitData);
|
|
514
|
+
report.unitStructure = {
|
|
515
|
+
status: 'valid',
|
|
516
|
+
hasAllFourKeys: true,
|
|
517
|
+
keys: Object.keys(unitData),
|
|
518
|
+
values: unitData
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// 物理的整合性検証
|
|
522
|
+
report.physicalConsistency = this.validatePhysicalConsistency(unitData);
|
|
523
|
+
|
|
524
|
+
// システム整合性検証(YAML提供時)
|
|
525
|
+
if (yamlData) {
|
|
526
|
+
report.systemIntegrity = this.validateSystemUnitIntegrity(unitData, yamlData);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 総合健康度判定
|
|
530
|
+
const hasCriticalIssues = report.physicalConsistency.warnings
|
|
531
|
+
.some(w => w.type === 'mixed_unit_system');
|
|
532
|
+
|
|
533
|
+
if (hasCriticalIssues) {
|
|
534
|
+
report.overallHealth = 'warning';
|
|
535
|
+
report.warnings.push('Mixed unit systems detected - may affect calculation accuracy');
|
|
536
|
+
} else if (report.physicalConsistency.warnings.length > 0) {
|
|
537
|
+
report.overallHealth = 'minor_warnings';
|
|
538
|
+
} else {
|
|
539
|
+
report.overallHealth = 'excellent';
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 推奨事項の生成
|
|
543
|
+
if (unitData.angle === 'degree') {
|
|
544
|
+
report.recommendations.push('Consider using radian for angle unit in scientific calculations');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (unitData.length !== 'cm' || unitData.density !== 'g/cm3') {
|
|
548
|
+
report.recommendations.push('Current setup uses mixed unit systems. CGS (cm + g/cm³) is commonly preferred in radiation calculations');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
} catch (error) {
|
|
552
|
+
report.unitStructure = {
|
|
553
|
+
status: 'invalid',
|
|
554
|
+
error: error.message
|
|
555
|
+
};
|
|
556
|
+
report.overallHealth = 'critical';
|
|
557
|
+
report.criticalIssues.push(error.message);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return report;
|
|
561
|
+
}
|
|
562
|
+
}
|