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.
Files changed (50) hide show
  1. package/README.md +241 -0
  2. package/config/mcp-manifest.json +1713 -0
  3. package/config/package-lock.json +1335 -0
  4. package/config/poker_mcp.code-workspace +8 -0
  5. package/package.json +51 -0
  6. package/src/config/ConfigManager.js +120 -0
  7. package/src/config/default.json +62 -0
  8. package/src/mcp/handlers/bodyHandlers.js +63 -0
  9. package/src/mcp/handlers/buildupFactorHandlers.js +43 -0
  10. package/src/mcp/handlers/calculationHandlers.js +265 -0
  11. package/src/mcp/handlers/detectorHandlers.js +201 -0
  12. package/src/mcp/handlers/index.js +43 -0
  13. package/src/mcp/handlers/sourceHandlers.js +25 -0
  14. package/src/mcp/handlers/transformHandlers.js +26 -0
  15. package/src/mcp/handlers/unitHandlers.js +305 -0
  16. package/src/mcp/handlers/zoneHandlers.js +83 -0
  17. package/src/mcp/middleware/errorHandler.js +49 -0
  18. package/src/mcp/middleware/requestValidator.js +129 -0
  19. package/src/mcp/server.js +76 -0
  20. package/src/mcp/tools/bodyTools.js +185 -0
  21. package/src/mcp/tools/buildupFactorTools.js +82 -0
  22. package/src/mcp/tools/calculationTools.js +58 -0
  23. package/src/mcp/tools/commonTools.js +22 -0
  24. package/src/mcp/tools/detectorTools.js +131 -0
  25. package/src/mcp/tools/index.js +34 -0
  26. package/src/mcp/tools/sourceTools.js +726 -0
  27. package/src/mcp/tools/sourceTools_fixed_part2.js +41 -0
  28. package/src/mcp/tools/transformTools.js +73 -0
  29. package/src/mcp/tools/unitTools.js +137 -0
  30. package/src/mcp/tools/zoneTools.js +65 -0
  31. package/src/mcp_server_stdio_v4.js +45 -0
  32. package/src/routes/restApi.js +184 -0
  33. package/src/services/CalculationService.js +525 -0
  34. package/src/services/DataManager.js +738 -0
  35. package/src/services/TaskManager.js +1622 -0
  36. package/src/utils/MaterialAlternatives.js +189 -0
  37. package/src/utils/errorHandler.js +68 -0
  38. package/src/utils/errors.js +37 -0
  39. package/src/utils/logger.js +24 -0
  40. package/src/utils/logger_no_color.js +28 -0
  41. package/src/utils/mcpErrors.js +225 -0
  42. package/src/validators/CMBValidator.js +191 -0
  43. package/src/validators/DetectorValidator.js +512 -0
  44. package/src/validators/ManifestValidator.js +249 -0
  45. package/src/validators/NuclideValidator.js +171 -0
  46. package/src/validators/PhysicsValidator.js +401 -0
  47. package/src/validators/PhysicsValidatorSimple.js +87 -0
  48. package/src/validators/SourceValidator.js +515 -0
  49. package/src/validators/TransformValidator.js +472 -0
  50. package/src/validators/UnitValidator.js +562 -0
@@ -0,0 +1,1622 @@
1
+ // services/TaskManager.js
2
+ import { SafeDataManager } from './DataManager.js';
3
+ import { PhysicsValidator } from '../validators/PhysicsValidator.js';
4
+ import { ManifestValidator } from '../validators/ManifestValidator.js';
5
+ import { TransformValidator } from '../validators/TransformValidator.js';
6
+ import { NuclideValidator } from '../validators/NuclideValidator.js';
7
+ import { SourceValidator } from '../validators/SourceValidator.js';
8
+ import { DetectorValidator } from '../validators/DetectorValidator.js';
9
+ import { UnitValidator } from '../validators/UnitValidator.js';
10
+ import { PokerMcpError } from '../utils/mcpErrors.js';
11
+ import { MaterialAlternatives } from '../utils/MaterialAlternatives.js';
12
+ import { logger } from '../utils/logger.js';
13
+ import { ValidationError, PhysicsError } from '../utils/errors.js';
14
+
15
+ export class TaskManager {
16
+ constructor(yamlFile, pendingFile) {
17
+ this.dataManager = new SafeDataManager(yamlFile, pendingFile);
18
+ }
19
+
20
+ /**
21
+ * Detector構造の分析ヘルパーメソッド
22
+ * DetectorHandlerから使用される簡易分析インターフェース
23
+ */
24
+ analyzeDetectorStructure(detectorData) {
25
+ try {
26
+ const validation = DetectorValidator.validateCompleteDetectorStructure(detectorData, this.data);
27
+ const optimization = DetectorValidator.analyzeDetectorOptimization(detectorData, this.data);
28
+
29
+ return {
30
+ name: validation.name,
31
+ dimension: validation.gridAnalysis ? validation.gridAnalysis.dimension : 0,
32
+ complexity: validation.complexity,
33
+ type: validation.type,
34
+ isOptimal: optimization.isOptimal,
35
+ suggestions: optimization.suggestions,
36
+ performance: optimization.performance
37
+ };
38
+
39
+ } catch (error) {
40
+ logger.error('Detector構造分析エラー', {
41
+ detectorName: detectorData.name,
42
+ error: error.message
43
+ });
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Detectorグリッドの互換性チェック
50
+ * 複数のDetector間でグリッド構造の互換性を分析
51
+ */
52
+ async analyzeDetectorCompatibility(detector1Name, detector2Name) {
53
+ try {
54
+ const detector1 = this.findDetectorByName(detector1Name);
55
+ const detector2 = this.findDetectorByName(detector2Name);
56
+
57
+ if (!detector1) {
58
+ throw new ValidationError(`検出器 ${detector1Name} が見つかりません`, 'detector1Name', detector1Name);
59
+ }
60
+
61
+ if (!detector2) {
62
+ throw new ValidationError(`検出器 ${detector2Name} が見つかりません`, 'detector2Name', detector2Name);
63
+ }
64
+
65
+ const compatibility = DetectorValidator.checkDetectorCompatibility(detector1, detector2);
66
+
67
+ logger.info('Detector互換性分析を実行しました', {
68
+ detector1: detector1Name,
69
+ detector2: detector2Name,
70
+ compatibility
71
+ });
72
+
73
+ return {
74
+ detector1: detector1Name,
75
+ detector2: detector2Name,
76
+ compatibility,
77
+ recommendations: this.generateCompatibilityRecommendations(compatibility)
78
+ };
79
+
80
+ } catch (error) {
81
+ logger.error('Detector互換性分析エラー', {
82
+ detector1Name,
83
+ detector2Name,
84
+ error: error.message
85
+ });
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * 互換性に基づいた推奨事項を生成
92
+ * @private
93
+ */
94
+ generateCompatibilityRecommendations(compatibility) {
95
+ const recommendations = [];
96
+
97
+ if (!compatibility.dimensionMatch) {
98
+ recommendations.push({
99
+ type: 'dimension_mismatch',
100
+ priority: 'high',
101
+ message: 'Detectors have different dimensions. Consider using detectors with matching dimensions for comparative analysis.'
102
+ });
103
+ }
104
+
105
+ if (!compatibility.resolutionCompatible) {
106
+ recommendations.push({
107
+ type: 'resolution_mismatch',
108
+ priority: 'medium',
109
+ message: 'Detector resolutions differ significantly. Results may not be directly comparable.'
110
+ });
111
+ }
112
+
113
+ if (!compatibility.geometryCompatible) {
114
+ recommendations.push({
115
+ type: 'geometry_mismatch',
116
+ priority: 'medium',
117
+ message: 'Different grid geometries (orthogonal vs oblique). Consider geometric corrections in analysis.'
118
+ });
119
+ }
120
+
121
+ if (compatibility.overall === 'fully_compatible') {
122
+ recommendations.push({
123
+ type: 'fully_compatible',
124
+ priority: 'info',
125
+ message: 'Detectors are fully compatible. Direct comparison and analysis is recommended.'
126
+ });
127
+ }
128
+
129
+ return recommendations;
130
+ }
131
+
132
+ /**
133
+ * システム全体のDetector性能分析
134
+ * 全てのDetectorの性能特性を分析
135
+ */
136
+ async analyzeSystemDetectorPerformance() {
137
+ try {
138
+ const detectors = this.data.detector || [];
139
+ const performanceAnalysis = {
140
+ totalDetectors: detectors.length,
141
+ dimensionDistribution: { '0D': 0, '1D': 0, '2D': 0, '3D': 0 },
142
+ totalComplexity: 0,
143
+ averageComplexity: 0,
144
+ highPerformanceDetectors: [],
145
+ lowPerformanceDetectors: [],
146
+ memoryEstimate: { total: 0, breakdown: [] },
147
+ processingRecommendations: []
148
+ };
149
+
150
+ if (detectors.length === 0) {
151
+ return performanceAnalysis;
152
+ }
153
+
154
+ for (const detector of detectors) {
155
+ const analysis = DetectorValidator.validateCompleteDetectorStructure(detector, this.data);
156
+ const optimization = DetectorValidator.analyzeDetectorOptimization(detector, this.data);
157
+
158
+ // 次元分布
159
+ const dimension = analysis.gridAnalysis ?
160
+ `${analysis.gridAnalysis.dimension}D` : '0D';
161
+ performanceAnalysis.dimensionDistribution[dimension]++;
162
+
163
+ // 複雑度統計
164
+ performanceAnalysis.totalComplexity += analysis.complexity;
165
+
166
+ // 性能分類
167
+ if (analysis.complexity > 10000) {
168
+ performanceAnalysis.highPerformanceDetectors.push({
169
+ name: detector.name,
170
+ complexity: analysis.complexity,
171
+ estimatedMemory: optimization.performance.estimatedMemory.total
172
+ });
173
+ } else if (analysis.complexity < 100) {
174
+ performanceAnalysis.lowPerformanceDetectors.push({
175
+ name: detector.name,
176
+ complexity: analysis.complexity
177
+ });
178
+ }
179
+
180
+ // メモリ使用量統計
181
+ performanceAnalysis.memoryEstimate.total += optimization.performance.estimatedMemory.total;
182
+ performanceAnalysis.memoryEstimate.breakdown.push({
183
+ detector: detector.name,
184
+ memory: optimization.performance.estimatedMemory.total
185
+ });
186
+ }
187
+
188
+ performanceAnalysis.averageComplexity =
189
+ performanceAnalysis.totalComplexity / detectors.length;
190
+
191
+ // 処理推奨事項の生成
192
+ if (performanceAnalysis.memoryEstimate.total > 1000) {
193
+ performanceAnalysis.processingRecommendations.push(
194
+ '高メモリ使用量が予想されます。分散処理または解像度の調整を検討してください。'
195
+ );
196
+ }
197
+
198
+ if (performanceAnalysis.highPerformanceDetectors.length > 5) {
199
+ performanceAnalysis.processingRecommendations.push(
200
+ '多数の高性能検出器があります。並列処理またはクラスター計算を推奨します。'
201
+ );
202
+ }
203
+
204
+ logger.info('システムDetector性能分析を完了しました', performanceAnalysis);
205
+
206
+ return performanceAnalysis;
207
+
208
+ } catch (error) {
209
+ logger.error('システムDetector性能分析エラー', { error: error.message });
210
+ throw error;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Transformの完全性チェック
216
+ * システム全体でTransform参照の整合性を確認
217
+ */
218
+ // Unit操作 - 4キー完全性保証機能強化版
219
+ async proposeUnit(length, angle, density, radioactivity) {
220
+ try {
221
+ // 4キー完全構造の構築
222
+ const unitData = {
223
+ length,
224
+ angle,
225
+ density,
226
+ radioactivity
227
+ };
228
+
229
+ // UnitValidatorによる4キー完全性検証
230
+ UnitValidator.validateFourKeyCompleteness(unitData);
231
+
232
+ // 既存単位設定の確認
233
+ if (this.data.unit) {
234
+ throw PokerMcpError.duplicateName('unit', 'unit section already exists');
235
+ }
236
+
237
+ // 物理的整合性検証
238
+ const physicalConsistency = UnitValidator.validatePhysicalConsistency(unitData);
239
+
240
+ // データ正規化
241
+ const normalizedUnit = UnitValidator.normalizeUnitStructure(unitData);
242
+
243
+ await this.dataManager.addPendingChange({
244
+ action: 'proposeUnit',
245
+ data: normalizedUnit
246
+ });
247
+
248
+ logger.info('4キー完全単位設定を提案しました', {
249
+ unitData: normalizedUnit,
250
+ physicalConsistency: physicalConsistency.isConsistent,
251
+ warnings: physicalConsistency.warnings.length
252
+ });
253
+
254
+ let responseMessage = '提案: 単位設定を追加(4キー完全性保証)';
255
+
256
+ if (physicalConsistency.warnings.length > 0) {
257
+ const warningTypes = physicalConsistency.warnings.map(w => w.type);
258
+ responseMessage += ` - 警告: ${warningTypes.join(', ')}`;
259
+ }
260
+
261
+ return responseMessage;
262
+
263
+ } catch (error) {
264
+ logger.error('単位設定提案エラー', { unitData: { length, angle, density, radioactivity }, error: error.message });
265
+ throw error;
266
+ }
267
+ }
268
+
269
+ async getUnit() {
270
+ try {
271
+ if (!this.data.unit) {
272
+ throw new ValidationError('単位設定が存在しません', 'unit', null);
273
+ }
274
+
275
+ // 4キー完全性の事後検証
276
+ UnitValidator.validateFourKeyCompleteness(this.data.unit);
277
+
278
+ logger.info('4キー完全単位設定を取得しました', { unit: this.data.unit });
279
+
280
+ return {
281
+ unit: this.data.unit,
282
+ integrity: '4-key-complete',
283
+ keys: Object.keys(this.data.unit)
284
+ };
285
+
286
+ } catch (error) {
287
+ logger.error('単位設定取得エラー', { error: error.message });
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Transformの完全性チェック
294
+ * システム全体でTransform参照の整合性を確認
295
+ */
296
+ async validateSystemTransformIntegrity() {
297
+ try {
298
+ const integrityResult = TransformValidator.validateTransformIntegrity(this.data);
299
+
300
+ if (!integrityResult.isValid) {
301
+ logger.warn('Transform参照の整合性問題が発見されました', {
302
+ issues: integrityResult.issues
303
+ });
304
+ }
305
+
306
+ return {
307
+ success: integrityResult.isValid,
308
+ issues: integrityResult.issues,
309
+ summary: {
310
+ availableTransforms: integrityResult.availableTransforms.length,
311
+ checkedReferences: integrityResult.checkedReferences,
312
+ problemCount: integrityResult.issues.length
313
+ }
314
+ };
315
+
316
+ } catch (error) {
317
+ logger.error('Transform整合性チェックエラー', { error: error.message });
318
+ throw error;
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Transform使用状況統計を取得
324
+ * システム内のTransform使用状況を分析
325
+ */
326
+ async getTransformUsageReport() {
327
+ try {
328
+ const usageStats = TransformValidator.getTransformUsageStats(this.data);
329
+
330
+ logger.info('Transform使用状況を取得しました', {
331
+ total: usageStats.totalTransforms,
332
+ used: usageStats.usedTransforms,
333
+ unused: usageStats.unusedTransforms.length
334
+ });
335
+
336
+ return {
337
+ summary: {
338
+ totalTransforms: usageStats.totalTransforms,
339
+ usedTransforms: usageStats.usedTransforms,
340
+ unusedTransforms: usageStats.unusedTransforms.length,
341
+ usageEfficiency: usageStats.totalTransforms > 0 ?
342
+ (usageStats.usedTransforms / usageStats.totalTransforms * 100).toFixed(1) + '%' : '0%'
343
+ },
344
+ unusedTransforms: usageStats.unusedTransforms,
345
+ detailedStats: usageStats.detailedStats,
346
+ mostUsedTransform: usageStats.summary.mostUsed,
347
+ averageUsage: usageStats.summary.averageUsage.toFixed(2)
348
+ };
349
+
350
+ } catch (error) {
351
+ logger.error('Transform使用統計エラー', { error: error.message });
352
+ throw error;
353
+ }
354
+ }
355
+
356
+ async updateUnit(updates) {
357
+ try {
358
+ if (!this.data.unit) {
359
+ throw new ValidationError('単位設定が存在しません', 'unit', null);
360
+ }
361
+
362
+ // 部分更新での4キー保持検証
363
+ const validationResult = UnitValidator.validatePartialUpdate(this.data.unit, updates);
364
+
365
+ // 更新後の物理的整合性検証
366
+ const physicalConsistency = UnitValidator.validatePhysicalConsistency(validationResult.updatedStructure);
367
+
368
+ // データ正規化
369
+ const normalizedUpdates = {};
370
+ for (const [key, value] of Object.entries(updates)) {
371
+ normalizedUpdates[key] = value.trim();
372
+ UnitValidator.validateSingleUnitKey(key, normalizedUpdates[key]);
373
+ }
374
+
375
+ await this.dataManager.addPendingChange({
376
+ action: 'updateUnit',
377
+ data: normalizedUpdates
378
+ });
379
+
380
+ logger.info('4キー保持単位更新を提案しました', {
381
+ updates: normalizedUpdates,
382
+ preservedIntegrity: validationResult.preservedIntegrity,
383
+ changedKeys: validationResult.changedKeys,
384
+ physicalConsistency: physicalConsistency.isConsistent
385
+ });
386
+
387
+ let responseMessage = `提案: 単位設定の更新(${validationResult.changedKeys.join(', ')})- 4キー完全性保持`;
388
+
389
+ if (physicalConsistency.warnings.length > 0) {
390
+ const warningTypes = physicalConsistency.warnings.map(w => w.type);
391
+ responseMessage += ` - 警告: ${warningTypes.join(', ')}`;
392
+ }
393
+
394
+ return responseMessage;
395
+
396
+ } catch (error) {
397
+ logger.error('単位設定更新エラー', { updates, error: error.message });
398
+ throw error;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * 単位系の4キー完全性と物理的整合性を包括検証
404
+ */
405
+ async validateUnitIntegrity(includeSystemAnalysis = true, generateReport = true) {
406
+ try {
407
+ if (!this.data.unit) {
408
+ throw new ValidationError('単位設定が存在しません。検証には単位設定が必要です', 'unit', null);
409
+ }
410
+
411
+ let report = null;
412
+ if (generateReport) {
413
+ report = UnitValidator.generateIntegrityDiagnosticReport(
414
+ this.data.unit,
415
+ includeSystemAnalysis ? this.data : null
416
+ );
417
+ }
418
+
419
+ // 基本4キー完全性検証
420
+ UnitValidator.validateFourKeyCompleteness(this.data.unit);
421
+
422
+ // 物理的整合性検証
423
+ const physicalConsistency = UnitValidator.validatePhysicalConsistency(this.data.unit);
424
+
425
+ // システム整合性検証
426
+ let systemIntegrity = null;
427
+ if (includeSystemAnalysis) {
428
+ systemIntegrity = UnitValidator.validateSystemUnitIntegrity(this.data.unit, this.data);
429
+ }
430
+
431
+ logger.info('単位系完全性検証を実行しました', {
432
+ hasAllFourKeys: true,
433
+ physicalConsistency: physicalConsistency.isConsistent,
434
+ warningCount: physicalConsistency.warnings.length,
435
+ systemAnalysis: !!systemIntegrity
436
+ });
437
+
438
+ return {
439
+ integrity: 'verified',
440
+ fourKeyComplete: true,
441
+ physicalConsistency,
442
+ systemIntegrity,
443
+ diagnosticReport: report,
444
+ summary: {
445
+ overallHealth: report ? report.overallHealth : 'verified',
446
+ criticalIssues: report ? report.criticalIssues.length : 0,
447
+ warnings: physicalConsistency.warnings.length,
448
+ recommendations: report ? report.recommendations.length : 0
449
+ }
450
+ };
451
+
452
+ } catch (error) {
453
+ logger.error('単位系完全性検証エラー', { error: error.message });
454
+ throw error;
455
+ }
456
+ }
457
+
458
+ /**
459
+ * 異なる単位系間の変換係数分析
460
+ */
461
+ async analyzeUnitConversion(targetUnits, includePhysicalAnalysis = true) {
462
+ try {
463
+ if (!this.data.unit) {
464
+ throw new ValidationError('現在の単位設定が存在しません', 'unit', null);
465
+ }
466
+
467
+ // 現在の単位系の4キー完全性検証
468
+ UnitValidator.validateFourKeyCompleteness(this.data.unit);
469
+
470
+ // 目標単位系の4キー完全性検証
471
+ UnitValidator.validateFourKeyCompleteness(targetUnits);
472
+
473
+ // 変換係数の計算
474
+ const conversionAnalysis = UnitValidator.calculateConversionFactors(
475
+ this.data.unit,
476
+ targetUnits
477
+ );
478
+
479
+ let physicalAnalysis = null;
480
+ if (includePhysicalAnalysis) {
481
+ // 両方の単位系の物理的整合性分析
482
+ const currentConsistency = UnitValidator.validatePhysicalConsistency(this.data.unit);
483
+ const targetConsistency = UnitValidator.validatePhysicalConsistency(targetUnits);
484
+
485
+ physicalAnalysis = {
486
+ current: currentConsistency,
487
+ target: targetConsistency,
488
+ conversionImpact: this.assessConversionImpact(conversionAnalysis)
489
+ };
490
+ }
491
+
492
+ logger.info('単位変換分析を実行しました', {
493
+ fromUnits: this.data.unit,
494
+ toUnits: targetUnits,
495
+ isIdentityConversion: conversionAnalysis.isIdentity,
496
+ hasPhysicalAnalysis: !!physicalAnalysis
497
+ });
498
+
499
+ return {
500
+ conversion: conversionAnalysis,
501
+ physicalAnalysis,
502
+ recommendations: this.generateConversionRecommendations(conversionAnalysis, physicalAnalysis)
503
+ };
504
+
505
+ } catch (error) {
506
+ logger.error('単位変換分析エラー', { targetUnits, error: error.message });
507
+ throw error;
508
+ }
509
+ }
510
+
511
+ /**
512
+ * 変換の影響評価
513
+ * @private
514
+ */
515
+ assessConversionImpact(conversionAnalysis) {
516
+ const impacts = [];
517
+
518
+ for (const [key, factor] of Object.entries(conversionAnalysis.factors)) {
519
+ if (Math.abs(factor - 1.0) > 1e-10) {
520
+ let impact = 'moderate';
521
+ if (Math.abs(Math.log10(Math.abs(factor))) > 2) {
522
+ impact = 'significant'; // 100倍以上の差
523
+ } else if (Math.abs(Math.log10(Math.abs(factor))) < 0.1) {
524
+ impact = 'minor'; // 1.26倍未満の差
525
+ }
526
+
527
+ impacts.push({
528
+ key,
529
+ factor,
530
+ impact,
531
+ description: `${key} units require ${factor.toExponential(3)} multiplication factor`
532
+ });
533
+ }
534
+ }
535
+
536
+ return impacts;
537
+ }
538
+
539
+ /**
540
+ * 変換推奨事項の生成
541
+ * @private
542
+ */
543
+ generateConversionRecommendations(conversionAnalysis, physicalAnalysis) {
544
+ const recommendations = [];
545
+
546
+ if (conversionAnalysis.isIdentity) {
547
+ recommendations.push({
548
+ type: 'no_conversion_needed',
549
+ priority: 'info',
550
+ message: 'No unit conversion required - units are identical'
551
+ });
552
+ return recommendations;
553
+ }
554
+
555
+ // 大きな変換係数の警告
556
+ const significantConversions = conversionAnalysis.factors;
557
+ for (const [key, factor] of Object.entries(significantConversions)) {
558
+ if (Math.abs(Math.log10(Math.abs(factor))) > 3) { // 1000倍以上
559
+ recommendations.push({
560
+ type: 'large_conversion_factor',
561
+ priority: 'high',
562
+ message: `Very large conversion factor for ${key}: ${factor.toExponential(2)}. Verify calculation accuracy.`
563
+ });
564
+ }
565
+ }
566
+
567
+ // 物理的整合性の比較
568
+ if (physicalAnalysis) {
569
+ if (physicalAnalysis.current.warnings.length < physicalAnalysis.target.warnings.length) {
570
+ recommendations.push({
571
+ type: 'target_system_warnings',
572
+ priority: 'medium',
573
+ message: 'Target unit system has more warnings than current system. Consider physical consistency.'
574
+ });
575
+ }
576
+ }
577
+
578
+ return recommendations;
579
+ }
580
+
581
+ async initialize() {
582
+ // ConfigManager初期化
583
+ try {
584
+ await this.dataManager.initialize();
585
+ logger.info('TaskManagerを初期化しました');
586
+ } catch (error) {
587
+ // ConfigManagerが見つからない場合は警告のみ
588
+ logger.warn('ConfigManagerの初期化をスキップしました', { error: error.message });
589
+ await this.dataManager.initialize();
590
+ logger.info('TaskManagerを初期化しました(ConfigManagerなし)');
591
+ }
592
+ }
593
+
594
+ // データアクセサ
595
+ get data() {
596
+ return this.dataManager.data;
597
+ }
598
+
599
+ get pendingChanges() {
600
+ return this.dataManager.pendingChanges;
601
+ }
602
+
603
+ // ヘルパーメソッド
604
+ findBodyByName(name) {
605
+ return this.data.body?.find(b => b.name === name);
606
+ }
607
+
608
+ // 全立体の取得(pending changes含む)
609
+ getAllBodies() {
610
+ const currentBodies = this.data.body || [];
611
+ const pendingBodies = this.dataManager.getPendingBodies() || [];
612
+ return [...currentBodies, ...pendingBodies];
613
+ }
614
+
615
+ findZoneByBodyName(bodyName) {
616
+ return this.data.zone?.find(z => z.body_name === bodyName);
617
+ }
618
+
619
+ findSourceByName(name) {
620
+ return this.data.source?.find(s => s.name === name);
621
+ }
622
+
623
+ findDetectorByName(name) {
624
+ return this.data.detector?.find(d => d.name === name);
625
+ }
626
+
627
+ validatePosition(position) {
628
+ if (typeof position !== 'string' || position.trim() === '') {
629
+ throw new ValidationError('positionは文字列で指定してください', 'position', position);
630
+ }
631
+ const coords = position.trim().split(/\s+/);
632
+ if (coords.length !== 3) {
633
+ throw new ValidationError('positionは3つの座標値(x y z)で指定してください', 'position', position);
634
+ }
635
+ for (const coord of coords) {
636
+ if (isNaN(Number(coord))) {
637
+ throw new ValidationError('positionの座標値は数値で指定してください', 'position', position);
638
+ }
639
+ }
640
+ }
641
+
642
+ validateBodyName(name) {
643
+ ManifestValidator.validateObjectName(name, 'body name');
644
+ }
645
+
646
+ // Sourceデータの正規化(座標正規化とgeometry/division処理)
647
+ normalizeSourceData(sourceData) {
648
+ const normalized = { ...sourceData };
649
+
650
+ logger.info('Sourceデータ正規化開始', {
651
+ name: sourceData.name,
652
+ type: sourceData.type,
653
+ hasGeometry: !!sourceData.geometry,
654
+ hasDivision: !!sourceData.division,
655
+ hasPosition: !!sourceData.position
656
+ });
657
+
658
+ // POINT線源の場合
659
+ if (sourceData.type === 'POINT') {
660
+ if (sourceData.position) {
661
+ normalized.position = this.normalizeCoordinates(sourceData.position);
662
+ logger.info('POINT線源position正規化', { position: normalized.position });
663
+ }
664
+ // POINT線源ではgeometryとdivisionを除去
665
+ delete normalized.geometry;
666
+ delete normalized.division;
667
+ logger.info('POINT線源のgeometry/divisionを除去');
668
+ return normalized;
669
+ }
670
+
671
+ // 体積線源のgeometry正規化
672
+ if (sourceData.geometry) {
673
+ normalized.geometry = this.normalizeGeometryData(sourceData.geometry);
674
+ logger.info('体積線源geometry正規化完了', { geometry: normalized.geometry });
675
+ } else {
676
+ logger.warn('体積線源にgeometryがありません', { name: sourceData.name, type: sourceData.type });
677
+ }
678
+
679
+ // divisionは正規化不要(数値データのみ)
680
+ if (sourceData.division) {
681
+ normalized.division = sourceData.division;
682
+ logger.info('体積線源division保持', { division: normalized.division });
683
+ } else {
684
+ logger.warn('体積線源にdivisionがありません', { name: sourceData.name, type: sourceData.type });
685
+ }
686
+
687
+ // cutoff_rateは必須パラメータのため、デフォルト値設定を削除
688
+ // バリデーション時に必須チェックが実行される
689
+
690
+ logger.info('Sourceデータ正規化完了', {
691
+ name: normalized.name,
692
+ type: normalized.type,
693
+ hasGeometry: !!normalized.geometry,
694
+ hasDivision: !!normalized.division,
695
+ hasPosition: !!normalized.position,
696
+ cutoff_rate: normalized.cutoff_rate
697
+ });
698
+
699
+ return normalized;
700
+ }
701
+
702
+ // Geometryデータの正規化
703
+ normalizeGeometryData(geometry) {
704
+ const normalized = { ...geometry };
705
+
706
+ // 座標関連フィールドの正規化
707
+ const coordinateFields = [
708
+ 'vertex', 'edge_1', 'edge_2', 'edge_3', // BOX用
709
+ 'min', 'max', // RPP用
710
+ 'center', // SPH用
711
+ 'bottom_center', 'height_vector' // RCC用
712
+ ];
713
+
714
+ for (const field of coordinateFields) {
715
+ if (normalized[field]) {
716
+ normalized[field] = this.normalizeCoordinates(normalized[field]);
717
+ }
718
+ }
719
+
720
+ return normalized;
721
+ }
722
+ normalizeCoordinates(coordString) {
723
+ if (typeof coordString !== 'string') return coordString;
724
+ // "x y z" 形式の文字列を数値として正規化
725
+ const coords = coordString.trim().split(/\s+/).map(Number);
726
+ if (coords.length === 3 && coords.every(n => !isNaN(n))) {
727
+ return coords.join(' ');
728
+ }
729
+ return coordString;
730
+ }
731
+
732
+ // Detectorデータの正規化
733
+ normalizeDetectorData(detectorData) {
734
+ const normalized = { ...detectorData };
735
+
736
+ // 基準位置の正規化
737
+ if (normalized.origin) {
738
+ normalized.origin = this.normalizeCoordinates(normalized.origin);
739
+ }
740
+
741
+ // グリッドデータの正規化
742
+ if (normalized.grid && Array.isArray(normalized.grid)) {
743
+ normalized.grid = normalized.grid.map(gridItem => ({
744
+ edge: this.normalizeCoordinates(gridItem.edge),
745
+ number: Number(gridItem.number)
746
+ }));
747
+ }
748
+
749
+ // show_path_traceは必須パラメータのため、デフォルト値設定を削除
750
+ // バリデーション時に必須チェックが実行される
751
+
752
+ // transformが空文字列の場合は削除
753
+ if (!normalized.transform) {
754
+ delete normalized.transform;
755
+ }
756
+
757
+ return normalized;
758
+ }
759
+ normalizeGridData(grid) {
760
+ if (!Array.isArray(grid)) return grid;
761
+
762
+ return grid.map(gridItem => ({
763
+ edge: this.normalizeCoordinates(gridItem.edge),
764
+ number: Number(gridItem.number)
765
+ }));
766
+ }
767
+
768
+ // 検出器バリデーション
769
+ validateDetectorData(name, origin, grid) {
770
+ // 名前バリデーション
771
+ if (!name || typeof name !== 'string' || name.trim() === '') {
772
+ throw new ValidationError('検出器名は必須です', 'name', name);
773
+ }
774
+ if (name.length > 50) {
775
+ throw new ValidationError('検出器名は50文字以内で指定してください', 'name', name);
776
+ }
777
+
778
+ // 座標バリデーション
779
+ this.validatePosition(origin);
780
+
781
+ // グリッドバリデーション
782
+ if (grid && Array.isArray(grid)) {
783
+ if (grid.length > 3) {
784
+ throw new ValidationError('gridの次元は3次元以下である必要があります', 'grid', grid);
785
+ }
786
+ for (const gridItem of grid) {
787
+ if (!gridItem.edge || !gridItem.number) {
788
+ throw new ValidationError('gridの各要素にはedgeとnumberが必要です', 'grid', gridItem);
789
+ }
790
+ this.validatePosition(gridItem.edge);
791
+ if (!Number.isInteger(gridItem.number) || gridItem.number <= 0) {
792
+ throw new ValidationError('gridのnumberは正の整数である必要があります', 'number', gridItem.number);
793
+ }
794
+ }
795
+ }
796
+ }
797
+
798
+ // グリッドデータのバリデーション(更新用)
799
+ validateGridData(grid) {
800
+ if (!Array.isArray(grid)) {
801
+ throw new ValidationError('gridは配列である必要があります', 'grid', grid);
802
+ }
803
+ if (grid.length > 3) {
804
+ throw new ValidationError('gridの次元は3次元以下である必要があります', 'grid', grid);
805
+ }
806
+ for (const gridItem of grid) {
807
+ if (!gridItem.edge || !gridItem.number) {
808
+ throw new ValidationError('gridの各要素にはedgeとnumberが必要です', 'grid', gridItem);
809
+ }
810
+ this.validatePosition(gridItem.edge);
811
+ if (!Number.isInteger(gridItem.number) || gridItem.number <= 0) {
812
+ throw new ValidationError('gridのnumberは正の整数である必要があります', 'number', gridItem.number);
813
+ }
814
+ }
815
+ }
816
+ // 立体関連メソッド
817
+ async proposeBody(name, type, options = {}) {
818
+ try {
819
+ // 入力バリデーション
820
+ this.validateBodyName(name);
821
+ ManifestValidator.validateBodyType(type, 'type');
822
+
823
+ // CMB専用処理
824
+ if (type === 'CMB') {
825
+ // 現在の立体一覧を取得(pending changesも含む)
826
+ const existingBodies = this.getAllBodies();
827
+
828
+ // CMB専用バリデーション(context付き)
829
+ const context = {
830
+ name,
831
+ existingBodies,
832
+ allBodies: existingBodies
833
+ };
834
+ PhysicsValidator.validateGeometry(type, options, context);
835
+ } else {
836
+ // 通常のバリデーション
837
+ PhysicsValidator.validateGeometry(type, options);
838
+ }
839
+
840
+ // Transform参照チェック
841
+ if (options.transform) {
842
+ TransformValidator.validateContextTransformReference(
843
+ options.transform,
844
+ this.data,
845
+ 'body',
846
+ name
847
+ );
848
+ }
849
+
850
+ // 重複チェック
851
+ if (this.findBodyByName(name)) {
852
+ throw PokerMcpError.duplicateName(name, 'body');
853
+ }
854
+
855
+ const newBody = this.createBodyObject(name, type, options);
856
+
857
+ await this.dataManager.addPendingChange({
858
+ action: 'add_body',
859
+ data: { body: newBody }
860
+ });
861
+
862
+ logger.info('立体を提案しました', { name, type });
863
+ return `提案: 立体 ${name} を追加`;
864
+
865
+ } catch (error) {
866
+ logger.error('立体提案エラー', { name, type, error: error.message });
867
+ throw error;
868
+ }
869
+ }
870
+
871
+ createBodyObject(name, type, options) {
872
+ const body = { name, type };
873
+
874
+ // 型別のパラメータ設定(座標の正規化適用)
875
+ switch (type) {
876
+ case 'SPH':
877
+ body.center = this.normalizeCoordinates(options.center);
878
+ body.radius = Number(options.radius);
879
+ break;
880
+ case 'RCC':
881
+ body.bottom_center = this.normalizeCoordinates(options.bottom_center);
882
+ body.height_vector = this.normalizeCoordinates(options.height_vector);
883
+ body.radius = Number(options.radius);
884
+ break;
885
+ case 'RPP':
886
+ body.min = this.normalizeCoordinates(options.min);
887
+ body.max = this.normalizeCoordinates(options.max);
888
+ break;
889
+ case 'BOX':
890
+ body.vertex = this.normalizeCoordinates(options.vertex);
891
+ body.edge_1 = this.normalizeCoordinates(options.edge_1);
892
+ body.edge_2 = this.normalizeCoordinates(options.edge_2);
893
+ body.edge_3 = this.normalizeCoordinates(options.edge_3);
894
+ break;
895
+ case 'CMB':
896
+ body.expression = options.expression;
897
+ break;
898
+ case 'TOR':
899
+ body.center = this.normalizeCoordinates(options.center);
900
+ body.normal = this.normalizeCoordinates(options.normal);
901
+ body.major_radius = Number(options.major_radius);
902
+ body.minor_radius_horizontal = Number(options.minor_radius_horizontal);
903
+ body.minor_radius_vertical = Number(options.minor_radius_vertical);
904
+ break;
905
+ case 'ELL':
906
+ body.center = this.normalizeCoordinates(options.center);
907
+ body.radius_vector_1 = this.normalizeCoordinates(options.radius_vector_1);
908
+ body.radius_vector_2 = this.normalizeCoordinates(options.radius_vector_2);
909
+ body.radius_vector_3 = this.normalizeCoordinates(options.radius_vector_3);
910
+ break;
911
+ case 'REC':
912
+ body.bottom_center = this.normalizeCoordinates(options.bottom_center);
913
+ body.height_vector = this.normalizeCoordinates(options.height_vector);
914
+ body.radius_vector_1 = this.normalizeCoordinates(options.radius_vector_1);
915
+ body.radius_vector_2 = this.normalizeCoordinates(options.radius_vector_2);
916
+ break;
917
+ case 'TRC':
918
+ body.bottom_center = this.normalizeCoordinates(options.bottom_center);
919
+ body.height_vector = this.normalizeCoordinates(options.height_vector);
920
+ body.bottom_radius = Number(options.bottom_radius);
921
+ body.top_radius = Number(options.top_radius);
922
+ break;
923
+ case 'WED':
924
+ body.vertex = this.normalizeCoordinates(options.vertex);
925
+ body.width_vector = this.normalizeCoordinates(options.width_vector);
926
+ body.height_vector = this.normalizeCoordinates(options.height_vector);
927
+ body.depth_vector = this.normalizeCoordinates(options.depth_vector);
928
+ break;
929
+ }
930
+
931
+ if (options.transform) {
932
+ body.transform = options.transform;
933
+ }
934
+
935
+ return body;
936
+ }
937
+ async deleteBody(name) {
938
+ try {
939
+ const body = this.findBodyByName(name);
940
+ if (!body) {
941
+ throw new ValidationError(`立体 ${name} が見つかりません`, 'name', name);
942
+ }
943
+
944
+ // 依存関係チェック
945
+ const dependentZones = this.data.zone?.filter(z => z.body_name === name) || [];
946
+ if (dependentZones.length > 0) {
947
+ throw new PhysicsError(
948
+ `立体 ${name} には依存するゾーンが存在します: ${dependentZones.map(z => z.body_name).join(', ')}`,
949
+ 'DEPENDENT_ZONES_EXIST'
950
+ );
951
+ }
952
+
953
+ await this.dataManager.addPendingChange({
954
+ action: 'delete_body',
955
+ data: { name: name }
956
+ });
957
+
958
+ logger.info('立体削除を提案しました', { name });
959
+ return `提案: 立体 ${name} を削除`;
960
+
961
+ } catch (error) {
962
+ logger.error('立体削除エラー', { name, error: error.message });
963
+ throw error;
964
+ }
965
+ }
966
+
967
+ // ゾーン関連メソッド
968
+ async proposeZone(body_name, material, density) {
969
+ try {
970
+ // バリデーション
971
+ this.validateZoneProposal(body_name, material, density);
972
+ PhysicsValidator.validateMaterialDensity(material, density);
973
+
974
+ const newZone = { body_name, material };
975
+ if (material !== 'VOID') {
976
+ newZone.density = Number(density);
977
+ }
978
+
979
+ await this.dataManager.addPendingChange({
980
+ action: 'add_zone',
981
+ data: { zone: newZone }
982
+ });
983
+
984
+ logger.info('ゾーンを提案しました', { body_name, material });
985
+ return `提案: ゾーン ${body_name} (${material}) を追加`;
986
+
987
+ } catch (error) {
988
+ logger.error('ゾーン提案エラー', { body_name, material, error: error.message });
989
+ throw error;
990
+ }
991
+ }
992
+
993
+ validateZoneProposal(body_name, material, density) {
994
+ if (body_name !== 'ATMOSPHERE') {
995
+ const body = this.findBodyByName(body_name);
996
+ if (!body) {
997
+ throw new ValidationError(`立体 ${body_name} が存在しません`, 'body_name', body_name);
998
+ }
999
+ }
1000
+
1001
+ const existingZone = this.findZoneByBodyName(body_name);
1002
+ if (existingZone) {
1003
+ throw new ValidationError(`ゾーン ${body_name} は既に存在します`, 'body_name', body_name);
1004
+ }
1005
+
1006
+ // 材料代替機能付きバリデーション
1007
+ try {
1008
+ MaterialAlternatives.validateMaterialWithSuggestion(material, 'material');
1009
+ } catch (error) {
1010
+ // MaterialAlternativesからのエラーをValidationErrorに変換
1011
+ throw new ValidationError(error.message, 'material', material);
1012
+ }
1013
+ }
1014
+
1015
+ async deleteZone(body_name) {
1016
+ try {
1017
+ if (body_name === 'ATMOSPHERE') {
1018
+ throw new ValidationError('ATMOSPHEREゾーンは削除できません', 'body_name', body_name);
1019
+ }
1020
+
1021
+ const zone = this.findZoneByBodyName(body_name);
1022
+ if (!zone) {
1023
+ throw new ValidationError(`ゾーン ${body_name} が見つかりません`, 'body_name', body_name);
1024
+ }
1025
+
1026
+ await this.dataManager.addPendingChange({
1027
+ action: 'delete_zone',
1028
+ data: { body_name: body_name }
1029
+ });
1030
+
1031
+ logger.info('ゾーン削除を提案しました', { body_name });
1032
+ return `提案: ゾーン ${body_name} を削除`;
1033
+
1034
+ } catch (error) {
1035
+ logger.error('ゾーン削除エラー', { body_name, error: error.message });
1036
+ throw error;
1037
+ }
1038
+ }
1039
+
1040
+ async applyChanges() {
1041
+ try {
1042
+ const result = await this.dataManager.applyChanges();
1043
+ logger.info('変更を適用しました');
1044
+ return result;
1045
+ } catch (error) {
1046
+ logger.error('変更適用エラー', { error: error.message });
1047
+ throw error;
1048
+ }
1049
+ }
1050
+
1051
+ // Transform操作
1052
+ async proposeTransform(name, operations) {
1053
+ try {
1054
+ if (!name) throw new ValidationError('回転移動のnameは必須です', 'name', name);
1055
+ if (!Array.isArray(operations) || operations.length === 0) {
1056
+ throw new ValidationError('operationは配列で1つ以上必要です', 'operation', operations);
1057
+ }
1058
+
1059
+ // 基本バリデーション
1060
+ TransformValidator.validateTransformOperations(operations);
1061
+
1062
+ // 一意性チェック
1063
+ TransformValidator.validateTransformUniqueness(name, this.data.transform, this.pendingChanges);
1064
+
1065
+ // 物理的妥当性チェック
1066
+ const physicsAnalysis = TransformValidator.validateTransformPhysics(operations, name);
1067
+
1068
+ await this.dataManager.addPendingChange({
1069
+ action: 'proposeTransform',
1070
+ data: { name, operations }
1071
+ });
1072
+
1073
+ logger.info('変換提案を追加しました', {
1074
+ name,
1075
+ operations,
1076
+ analysis: physicsAnalysis
1077
+ });
1078
+
1079
+ return `回転移動 ${name} を追加しました - 操作数: ${physicsAnalysis.operationCount}, 回転: ${physicsAnalysis.hasRotation}, 移動: ${physicsAnalysis.hasTranslation}`;
1080
+
1081
+ } catch (error) {
1082
+ logger.error('変換提案エラー', { name, error: error.message });
1083
+ throw error;
1084
+ }
1085
+ }
1086
+
1087
+ async updateTransform(name, updates) {
1088
+ try {
1089
+ if (!name) throw new ValidationError('変換のnameは必須です', 'name', name);
1090
+
1091
+ await this.dataManager.addPendingChange({
1092
+ action: 'updateTransform',
1093
+ data: { name, ...updates }
1094
+ });
1095
+ logger.info('変換更新を提案しました', { name, updates });
1096
+ return `提案: 変換 ${name} の更新を保留しました: ${JSON.stringify(updates)}`;
1097
+
1098
+ } catch (error) {
1099
+ logger.error('変換更新エラー', { name, error: error.message });
1100
+ throw error;
1101
+ }
1102
+ }
1103
+
1104
+ async deleteTransform(name) {
1105
+ try {
1106
+ if (!name) throw PokerMcpError.validationError('変換のnameは必須です', 'name', name);
1107
+
1108
+ // Transform名の検証
1109
+ ManifestValidator.validateObjectName(name, 'transform name');
1110
+
1111
+ // 依存関係チェック(保留中変更も含む)
1112
+ TransformValidator.checkTransformDependencies(name, this.data, this.pendingChanges);
1113
+
1114
+ await this.dataManager.addPendingChange({
1115
+ action: 'deleteTransform',
1116
+ data: { name }
1117
+ });
1118
+ logger.info('変換削除を提案しました', { name });
1119
+ return `提案: 変換 ${name} の削除を保留しました`;
1120
+
1121
+ } catch (error) {
1122
+ logger.error('変換削除エラー', { name, error: error.message });
1123
+ throw error;
1124
+ }
1125
+ }
1126
+
1127
+ // Buildup Factor操作
1128
+ async proposeBuildupFactor(material, useSlantCorrection, useFiniteMediumCorrection) {
1129
+ try {
1130
+ if (!material) throw new ValidationError('materialは必須です', 'material', material);
1131
+
1132
+ // 材料代替機能付きバリデーション
1133
+ try {
1134
+ MaterialAlternatives.validateMaterialWithSuggestion(material, 'material');
1135
+ } catch (error) {
1136
+ // MaterialAlternativesからのエラーをValidationErrorに変換
1137
+ throw new ValidationError(error.message, 'material', material);
1138
+ }
1139
+
1140
+ await this.dataManager.addPendingChange({
1141
+ action: 'proposeBuildupFactor',
1142
+ data: {
1143
+ material,
1144
+ use_slant_correction: useSlantCorrection,
1145
+ use_finite_medium_correction: useFiniteMediumCorrection
1146
+ }
1147
+ });
1148
+ logger.info('ビルドアップ係数提案を追加しました', { material });
1149
+ return `ビルドアップ係数 ${material} を追加しました`;
1150
+
1151
+ } catch (error) {
1152
+ logger.error('ビルドアップ係数提案エラー', { material, error: error.message });
1153
+ throw error;
1154
+ }
1155
+ }
1156
+
1157
+ async updateBuildupFactor(material, updates) {
1158
+ try {
1159
+ if (!material) throw new ValidationError('materialは必須です', 'material', material);
1160
+
1161
+ await this.dataManager.addPendingChange({
1162
+ action: 'updateBuildupFactor',
1163
+ data: { material, ...updates }
1164
+ });
1165
+ logger.info('ビルドアップ係数更新を提案しました', { material, updates });
1166
+ return `提案: ビルドアップ係数 ${material} の更新を保留しました`;
1167
+
1168
+ } catch (error) {
1169
+ logger.error('ビルドアップ係数更新エラー', { material, error: error.message });
1170
+ throw error;
1171
+ }
1172
+ }
1173
+
1174
+ async deleteBuildupFactor(material) {
1175
+ try {
1176
+ if (!material) throw new ValidationError('materialは必須です', 'material', material);
1177
+
1178
+ await this.dataManager.addPendingChange({
1179
+ action: 'deleteBuildupFactor',
1180
+ data: { material }
1181
+ });
1182
+ logger.info('ビルドアップ係数削除を提案しました', { material });
1183
+ return `提案: ビルドアップ係数 ${material} の削除を保留しました`;
1184
+
1185
+ } catch (error) {
1186
+ logger.error('ビルドアップ係数削除エラー', { material, error: error.message });
1187
+ throw error;
1188
+ }
1189
+ }
1190
+
1191
+ async changeOrderBuildupFactor(material, newIndex) {
1192
+ try {
1193
+ if (!material) throw new ValidationError('materialは必須です', 'material', material);
1194
+
1195
+ await this.dataManager.addPendingChange({
1196
+ action: 'changeOrderBuildupFactor',
1197
+ data: { material, newIndex }
1198
+ });
1199
+ logger.info('ビルドアップ係数順序変更を提案しました', { material, newIndex });
1200
+ return `提案: ビルドアップ係数 ${material} の順序変更を保留しました`;
1201
+
1202
+ } catch (error) {
1203
+ logger.error('ビルドアップ係数順序変更エラー', { material, error: error.message });
1204
+ throw error;
1205
+ }
1206
+ }
1207
+
1208
+ // Source操作
1209
+ async proposeSource(params) {
1210
+ try {
1211
+ const { name, type, position, geometry, division, inventory, cutoff_rate } = params;
1212
+
1213
+ logger.info('proposeSource開始', {
1214
+ name,
1215
+ type,
1216
+ hasPosition: !!position,
1217
+ hasGeometry: !!geometry,
1218
+ hasDivision: !!division,
1219
+ hasInventory: !!inventory,
1220
+ cutoff_rate
1221
+ });
1222
+
1223
+ // 基本バリデーション
1224
+ if (!name) throw PokerMcpError.validationError('線源のnameは必須です', 'name', name);
1225
+ ManifestValidator.validateObjectName(name, 'source name');
1226
+
1227
+ if (!type) throw PokerMcpError.validationError('線源のtypeは必須です', 'type', type);
1228
+ if (!inventory) throw PokerMcpError.validationError('線源のinventoryは必須です', 'inventory', inventory);
1229
+
1230
+ // Source構造の包括的検証(SourceValidatorを使用)
1231
+ const validationResult = SourceValidator.validateCompleteSourceStructure(params);
1232
+ logger.info('SourceValidator検証結果', validationResult);
1233
+
1234
+ // 重複チェック
1235
+ if (this.findSourceByName(name)) {
1236
+ throw PokerMcpError.duplicateName(name, 'source');
1237
+ }
1238
+
1239
+ // Transform参照チェック(geometry内にある場合)
1240
+ if (geometry && geometry.transform) {
1241
+ TransformValidator.validateContextTransformReference(
1242
+ geometry.transform,
1243
+ this.data,
1244
+ 'source',
1245
+ name
1246
+ );
1247
+ }
1248
+
1249
+ // Source構造の最適化分析
1250
+ const optimization = SourceValidator.analyzeSrcStructureOptimization(params);
1251
+ if (optimization.suggestions.length > 0) {
1252
+ logger.info('Source構造最適化の提案', {
1253
+ name,
1254
+ suggestions: optimization.suggestions
1255
+ });
1256
+ }
1257
+
1258
+ // データ正規化の前にパラメーター確認
1259
+ logger.info('正規化前のパラメーター', {
1260
+ name, type, position,
1261
+ geometryExists: !!geometry,
1262
+ divisionExists: !!division,
1263
+ geometryContent: geometry ? JSON.stringify(geometry) : 'null',
1264
+ divisionContent: division ? JSON.stringify(division) : 'null'
1265
+ });
1266
+
1267
+ // データ正規化
1268
+ const normalizedParams = this.normalizeSourceData(params);
1269
+
1270
+ logger.info('正規化後のパラメーター', {
1271
+ name: normalizedParams.name,
1272
+ type: normalizedParams.type,
1273
+ geometryExists: !!normalizedParams.geometry,
1274
+ divisionExists: !!normalizedParams.division,
1275
+ geometryContent: normalizedParams.geometry ? JSON.stringify(normalizedParams.geometry) : 'null',
1276
+ divisionContent: normalizedParams.division ? JSON.stringify(normalizedParams.division) : 'null'
1277
+ });
1278
+
1279
+ await this.dataManager.addPendingChange({
1280
+ action: 'proposeSource',
1281
+ data: normalizedParams
1282
+ });
1283
+
1284
+ logger.info('線源提案を追加しました', {
1285
+ name,
1286
+ type,
1287
+ validation: validationResult,
1288
+ optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions',
1289
+ finalHasGeometry: !!normalizedParams.geometry,
1290
+ finalHasDivision: !!normalizedParams.division
1291
+ });
1292
+
1293
+ const complexityInfo = validationResult.divisionComplexity > 1 ?
1294
+ ` (分割複雑度: ${validationResult.divisionComplexity})` : '';
1295
+
1296
+ return `提案: 線源 ${name} (${type}${complexityInfo}) を追加`;
1297
+
1298
+ } catch (error) {
1299
+ logger.error('線源提案エラー', { params, error: error.message });
1300
+ throw error;
1301
+ }
1302
+ }
1303
+
1304
+ async updateSource(name, updates) {
1305
+ try {
1306
+ if (!name) throw new ValidationError('線源のnameは必須です', 'name', name);
1307
+
1308
+ // 既存線源の存在確認
1309
+ const existingSource = this.findSourceByName(name);
1310
+ if (!existingSource) {
1311
+ throw new ValidationError(`線源 ${name} が見つかりません`, 'name', name);
1312
+ }
1313
+
1314
+ // 更新内容のバリデーションと正規化
1315
+ const normalizedUpdates = { ...updates };
1316
+
1317
+ // POINT線源のposition更新
1318
+ if (normalizedUpdates.position) {
1319
+ this.validatePosition(normalizedUpdates.position);
1320
+ normalizedUpdates.position = this.normalizeCoordinates(normalizedUpdates.position);
1321
+ }
1322
+
1323
+ // インベントリの更新
1324
+ if (normalizedUpdates.inventory) {
1325
+ NuclideValidator.validateInventory(normalizedUpdates.inventory);
1326
+ }
1327
+
1328
+ // geometryの更新
1329
+ if (normalizedUpdates.geometry) {
1330
+ // 既存Sourceのtypeを取得してgeometry検証
1331
+ const sourceType = existingSource.type;
1332
+ SourceValidator.validateSourceGeometry(sourceType, normalizedUpdates.geometry);
1333
+ normalizedUpdates.geometry = this.normalizeGeometryData(normalizedUpdates.geometry);
1334
+
1335
+ // Transform参照チェック
1336
+ if (normalizedUpdates.geometry.transform) {
1337
+ TransformValidator.validateContextTransformReference(
1338
+ normalizedUpdates.geometry.transform,
1339
+ this.data,
1340
+ 'source',
1341
+ name
1342
+ );
1343
+ }
1344
+ }
1345
+
1346
+ // divisionの更新
1347
+ if (normalizedUpdates.division) {
1348
+ const sourceType = existingSource.type;
1349
+ SourceValidator.validateSourceDivision(sourceType, normalizedUpdates.division);
1350
+ }
1351
+
1352
+ // cutoff_rateの更新
1353
+ if (normalizedUpdates.cutoff_rate !== undefined) {
1354
+ SourceValidator.validateCutoffRate(normalizedUpdates.cutoff_rate);
1355
+ }
1356
+
1357
+ // typeの変更は禁止(物理的整合性のため)
1358
+ if (normalizedUpdates.type && normalizedUpdates.type !== existingSource.type) {
1359
+ throw new ValidationError('線源のtypeは変更できません', 'type', normalizedUpdates.type);
1360
+ }
1361
+
1362
+ // 更新後の構造最適化分析
1363
+ const mergedSource = { ...existingSource, ...normalizedUpdates };
1364
+ const optimization = SourceValidator.analyzeSrcStructureOptimization(mergedSource);
1365
+ if (optimization.suggestions.length > 0) {
1366
+ logger.info('Source更新後の最適化提案', {
1367
+ name,
1368
+ suggestions: optimization.suggestions
1369
+ });
1370
+ }
1371
+
1372
+ await this.dataManager.addPendingChange({
1373
+ action: 'updateSource',
1374
+ data: { name, ...normalizedUpdates }
1375
+ });
1376
+
1377
+ logger.info('線源更新を提案しました', { name, updates: normalizedUpdates });
1378
+ return `提案: 線源 ${name} の更新を保留しました`;
1379
+
1380
+ } catch (error) {
1381
+ logger.error('線源更新エラー', { name, error: error.message });
1382
+ throw error;
1383
+ }
1384
+ }
1385
+
1386
+ async deleteSource(name) {
1387
+ try {
1388
+ if (!name) throw new ValidationError('線源のnameは必須です', 'name', name);
1389
+
1390
+ // 既存線源の存在確認
1391
+ const existingSource = this.findSourceByName(name);
1392
+ if (!existingSource) {
1393
+ throw new ValidationError(`線源 ${name} が見つかりません`, 'name', name);
1394
+ }
1395
+
1396
+ // 依存関係チェック(将来の拡張用)
1397
+ // 現在は線源を直接参照する他要素はないが、将来の機能追加に備える
1398
+
1399
+ await this.dataManager.addPendingChange({
1400
+ action: 'deleteSource',
1401
+ data: { name }
1402
+ });
1403
+ logger.info('線源削除を提案しました', { name });
1404
+ return `提案: 線源 ${name} を削除`;
1405
+
1406
+ } catch (error) {
1407
+ logger.error('線源削除エラー', { name, error: error.message });
1408
+ throw error;
1409
+ }
1410
+ }
1411
+
1412
+ // Body操作の追加メソッド
1413
+ async updateBody(name, updates) {
1414
+ try {
1415
+ if (!name) throw new ValidationError('立体のnameは必須です', 'name', name);
1416
+
1417
+ // 座標データの正規化
1418
+ const normalizedUpdates = { ...updates };
1419
+ if (normalizedUpdates.min) normalizedUpdates.min = this.normalizeCoordinates(normalizedUpdates.min);
1420
+ if (normalizedUpdates.max) normalizedUpdates.max = this.normalizeCoordinates(normalizedUpdates.max);
1421
+ if (normalizedUpdates.center) normalizedUpdates.center = this.normalizeCoordinates(normalizedUpdates.center);
1422
+ if (normalizedUpdates.bottom_center) normalizedUpdates.bottom_center = this.normalizeCoordinates(normalizedUpdates.bottom_center);
1423
+ if (normalizedUpdates.height_vector) normalizedUpdates.height_vector = this.normalizeCoordinates(normalizedUpdates.height_vector);
1424
+ if (normalizedUpdates.vertex) normalizedUpdates.vertex = this.normalizeCoordinates(normalizedUpdates.vertex);
1425
+ if (normalizedUpdates.edge_1) normalizedUpdates.edge_1 = this.normalizeCoordinates(normalizedUpdates.edge_1);
1426
+ if (normalizedUpdates.edge_2) normalizedUpdates.edge_2 = this.normalizeCoordinates(normalizedUpdates.edge_2);
1427
+ if (normalizedUpdates.edge_3) normalizedUpdates.edge_3 = this.normalizeCoordinates(normalizedUpdates.edge_3);
1428
+ // 新しい立体タイプのパラメータ対応
1429
+ if (normalizedUpdates.normal) normalizedUpdates.normal = this.normalizeCoordinates(normalizedUpdates.normal);
1430
+ if (normalizedUpdates.radius_vector_1) normalizedUpdates.radius_vector_1 = this.normalizeCoordinates(normalizedUpdates.radius_vector_1);
1431
+ if (normalizedUpdates.radius_vector_2) normalizedUpdates.radius_vector_2 = this.normalizeCoordinates(normalizedUpdates.radius_vector_2);
1432
+ if (normalizedUpdates.radius_vector_3) normalizedUpdates.radius_vector_3 = this.normalizeCoordinates(normalizedUpdates.radius_vector_3);
1433
+ if (normalizedUpdates.width_vector) normalizedUpdates.width_vector = this.normalizeCoordinates(normalizedUpdates.width_vector);
1434
+ if (normalizedUpdates.depth_vector) normalizedUpdates.depth_vector = this.normalizeCoordinates(normalizedUpdates.depth_vector);
1435
+
1436
+ await this.dataManager.addPendingChange({
1437
+ action: 'updateBody',
1438
+ data: { name, ...normalizedUpdates }
1439
+ });
1440
+ logger.info('立体更新を提案しました', { name, updates: normalizedUpdates });
1441
+ return `提案: 立体 ${name} の更新を保留しました: ${JSON.stringify(normalizedUpdates)}`;
1442
+
1443
+ } catch (error) {
1444
+ logger.error('立体更新エラー', { name, error: error.message });
1445
+ throw error;
1446
+ }
1447
+ }
1448
+
1449
+ async updateZone(bodyName, updates) {
1450
+ try {
1451
+ if (!bodyName) throw new ValidationError('ゾーンのbody_nameは必須です', 'body_name', bodyName);
1452
+
1453
+ await this.dataManager.addPendingChange({
1454
+ action: 'updateZone',
1455
+ data: { body_name: bodyName, ...updates }
1456
+ });
1457
+ logger.info('ゾーン更新を提案しました', { bodyName, updates });
1458
+ return `提案: ゾーン ${bodyName} の更新を保留しました: ${JSON.stringify(updates)}`;
1459
+
1460
+ } catch (error) {
1461
+ logger.error('ゾーン更新エラー', { bodyName, error: error.message });
1462
+ throw error;
1463
+ }
1464
+ }
1465
+
1466
+ // Detector操作
1467
+ async proposeDetector(name, origin, grid = [], show_path_trace = false, transform = null) {
1468
+ try {
1469
+ // 基本パラメータの構築
1470
+ const detectorData = {
1471
+ name,
1472
+ origin,
1473
+ grid,
1474
+ show_path_trace,
1475
+ transform
1476
+ };
1477
+
1478
+ // DetectorValidatorによる包括的検証
1479
+ const validationResult = DetectorValidator.validateCompleteDetectorStructure(
1480
+ detectorData,
1481
+ this.data
1482
+ );
1483
+
1484
+ // 重複チェック
1485
+ if (this.findDetectorByName(name)) {
1486
+ throw PokerMcpError.duplicateName(name, 'detector');
1487
+ }
1488
+
1489
+ // Transform参照チェック(指定されている場合)
1490
+ if (transform) {
1491
+ DetectorValidator.validateDetectorTransform(transform, this.data, name);
1492
+ }
1493
+
1494
+ // Detector最適化分析
1495
+ const optimization = DetectorValidator.analyzeDetectorOptimization(detectorData, this.data);
1496
+
1497
+ if (optimization.suggestions.length > 0) {
1498
+ logger.info('Detector構造最適化の提案', {
1499
+ name,
1500
+ suggestions: optimization.suggestions
1501
+ });
1502
+ }
1503
+
1504
+ // データ正規化
1505
+ const normalizedDetector = this.normalizeDetectorData(detectorData);
1506
+
1507
+ await this.dataManager.addPendingChange({
1508
+ action: 'proposeDetector',
1509
+ data: normalizedDetector
1510
+ });
1511
+
1512
+ logger.info('検出器提案を追加しました', {
1513
+ name,
1514
+ validation: validationResult,
1515
+ optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions',
1516
+ performance: optimization.performance
1517
+ });
1518
+
1519
+ const complexityInfo = validationResult.complexity > 1 ?
1520
+ ` (複雑度: ${validationResult.complexity})` : '';
1521
+ const dimensionInfo = validationResult.gridAnalysis ?
1522
+ ` ${validationResult.gridAnalysis.dimension}D` : ' Point';
1523
+
1524
+ return `提案: 検出器 ${name}${dimensionInfo}${complexityInfo} を追加`;
1525
+
1526
+ } catch (error) {
1527
+ logger.error('検出器提案エラー', { name, origin, error: error.message });
1528
+ throw error;
1529
+ }
1530
+ }
1531
+
1532
+ async updateDetector(name, updates) {
1533
+ try {
1534
+ if (!name) throw new ValidationError('検出器のnameは必須です', 'name', name);
1535
+
1536
+ // 既存検出器の存在確認
1537
+ const existingDetector = this.findDetectorByName(name);
1538
+ if (!existingDetector) {
1539
+ throw new ValidationError(`検出器 ${name} が見つかりません`, 'name', name);
1540
+ }
1541
+
1542
+ // 更新データの構築
1543
+ const updatedDetectorData = { ...existingDetector, ...updates, name };
1544
+
1545
+ // DetectorValidatorによる更新後データの検証
1546
+ const validationResult = DetectorValidator.validateCompleteDetectorStructure(
1547
+ updatedDetectorData,
1548
+ this.data
1549
+ );
1550
+
1551
+ // Transform参照チェック(更新される場合)
1552
+ if (updates.transform !== undefined) {
1553
+ if (updates.transform) {
1554
+ DetectorValidator.validateDetectorTransform(updates.transform, this.data, name);
1555
+ }
1556
+ }
1557
+
1558
+ // 更新後の最適化分析
1559
+ const optimization = DetectorValidator.analyzeDetectorOptimization(updatedDetectorData, this.data);
1560
+
1561
+ if (optimization.suggestions.length > 0) {
1562
+ logger.info('Detector更新後の最適化提案', {
1563
+ name,
1564
+ suggestions: optimization.suggestions
1565
+ });
1566
+ }
1567
+
1568
+ // データ正規化
1569
+ const normalizedUpdates = this.normalizeDetectorData(updates);
1570
+
1571
+ // nameフィールドは削除(名前変更不可)
1572
+ delete normalizedUpdates.name;
1573
+
1574
+ await this.dataManager.addPendingChange({
1575
+ action: 'updateDetector',
1576
+ data: { name, ...normalizedUpdates }
1577
+ });
1578
+
1579
+ logger.info('検出器更新を提案しました', {
1580
+ name,
1581
+ updates: normalizedUpdates,
1582
+ validation: validationResult,
1583
+ optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions'
1584
+ });
1585
+
1586
+ const complexityChange = validationResult.complexity !== existingDetector.grid?.length ?
1587
+ ` (複雑度: ${validationResult.complexity})` : '';
1588
+
1589
+ return `提案: 検出器 ${name} の更新を保留しました${complexityChange}`;
1590
+
1591
+ } catch (error) {
1592
+ logger.error('検出器更新エラー', { name, error: error.message });
1593
+ throw error;
1594
+ }
1595
+ }
1596
+
1597
+ async deleteDetector(name) {
1598
+ try {
1599
+ if (!name) throw new ValidationError('検出器のnameは必須です', 'name', name);
1600
+
1601
+ // 既存検出器の存在確認
1602
+ const existingDetector = this.findDetectorByName(name);
1603
+ if (!existingDetector) {
1604
+ throw new ValidationError(`検出器 ${name} が見つかりません`, 'name', name);
1605
+ }
1606
+
1607
+ // 依存関係チェック(将来の拡張用)
1608
+ // 現在は検出器を直接参照する他要素はないが、将来の機能追加に備える
1609
+
1610
+ await this.dataManager.addPendingChange({
1611
+ action: 'deleteDetector',
1612
+ data: { name }
1613
+ });
1614
+ logger.info('検出器削除を提案しました', { name });
1615
+ return `提案: 検出器 ${name} を削除`;
1616
+
1617
+ } catch (error) {
1618
+ logger.error('検出器削除エラー', { name, error: error.message });
1619
+ throw error;
1620
+ }
1621
+ }
1622
+ }