Skip to main content
Glama

Poker Task Management MCP

by Hirao-Y
TaskManager.js70 kB
// services/TaskManager.js import { SafeDataManager } from './DataManager.js'; import { PhysicsValidator } from '../validators/PhysicsValidator.js'; import { ManifestValidator } from '../validators/ManifestValidator.js'; import { TransformValidator } from '../validators/TransformValidator.js'; import { NuclideValidator } from '../validators/NuclideValidator.js'; import { SourceValidator } from '../validators/SourceValidator.js'; import { DetectorValidator } from '../validators/DetectorValidator.js'; import { UnitValidator } from '../validators/UnitValidator.js'; import { PokerMcpError } from '../utils/mcpErrors.js'; import { MaterialAlternatives } from '../utils/MaterialAlternatives.js'; import { logger } from '../utils/logger.js'; import { ValidationError, PhysicsError } from '../utils/errors.js'; export class TaskManager { constructor(yamlFile, pendingFile) { this.dataManager = new SafeDataManager(yamlFile, pendingFile); } /** * Detector構造の分析ヘルパーメソッド * DetectorHandlerから使用される簡易分析インターフェース */ analyzeDetectorStructure(detectorData) { try { const validation = DetectorValidator.validateCompleteDetectorStructure(detectorData, this.data); const optimization = DetectorValidator.analyzeDetectorOptimization(detectorData, this.data); return { name: validation.name, dimension: validation.gridAnalysis ? validation.gridAnalysis.dimension : 0, complexity: validation.complexity, type: validation.type, isOptimal: optimization.isOptimal, suggestions: optimization.suggestions, performance: optimization.performance }; } catch (error) { logger.error('Detector構造分析エラー', { detectorName: detectorData.name, error: error.message }); throw error; } } /** * Detectorグリッドの互換性チェック * 複数のDetector間でグリッド構造の互換性を分析 */ async analyzeDetectorCompatibility(detector1Name, detector2Name) { try { const detector1 = this.findDetectorByName(detector1Name); const detector2 = this.findDetectorByName(detector2Name); if (!detector1) { throw new ValidationError(`検出器 ${detector1Name} が見つかりません`, 'detector1Name', detector1Name); } if (!detector2) { throw new ValidationError(`検出器 ${detector2Name} が見つかりません`, 'detector2Name', detector2Name); } const compatibility = DetectorValidator.checkDetectorCompatibility(detector1, detector2); logger.info('Detector互換性分析を実行しました', { detector1: detector1Name, detector2: detector2Name, compatibility }); return { detector1: detector1Name, detector2: detector2Name, compatibility, recommendations: this.generateCompatibilityRecommendations(compatibility) }; } catch (error) { logger.error('Detector互換性分析エラー', { detector1Name, detector2Name, error: error.message }); throw error; } } /** * 互換性に基づいた推奨事項を生成 * @private */ generateCompatibilityRecommendations(compatibility) { const recommendations = []; if (!compatibility.dimensionMatch) { recommendations.push({ type: 'dimension_mismatch', priority: 'high', message: 'Detectors have different dimensions. Consider using detectors with matching dimensions for comparative analysis.' }); } if (!compatibility.resolutionCompatible) { recommendations.push({ type: 'resolution_mismatch', priority: 'medium', message: 'Detector resolutions differ significantly. Results may not be directly comparable.' }); } if (!compatibility.geometryCompatible) { recommendations.push({ type: 'geometry_mismatch', priority: 'medium', message: 'Different grid geometries (orthogonal vs oblique). Consider geometric corrections in analysis.' }); } if (compatibility.overall === 'fully_compatible') { recommendations.push({ type: 'fully_compatible', priority: 'info', message: 'Detectors are fully compatible. Direct comparison and analysis is recommended.' }); } return recommendations; } /** * システム全体のDetector性能分析 * 全てのDetectorの性能特性を分析 */ async analyzeSystemDetectorPerformance() { try { const detectors = this.data.detector || []; const performanceAnalysis = { totalDetectors: detectors.length, dimensionDistribution: { '0D': 0, '1D': 0, '2D': 0, '3D': 0 }, totalComplexity: 0, averageComplexity: 0, highPerformanceDetectors: [], lowPerformanceDetectors: [], memoryEstimate: { total: 0, breakdown: [] }, processingRecommendations: [] }; if (detectors.length === 0) { return performanceAnalysis; } for (const detector of detectors) { const analysis = DetectorValidator.validateCompleteDetectorStructure(detector, this.data); const optimization = DetectorValidator.analyzeDetectorOptimization(detector, this.data); // 次元分布 const dimension = analysis.gridAnalysis ? `${analysis.gridAnalysis.dimension}D` : '0D'; performanceAnalysis.dimensionDistribution[dimension]++; // 複雑度統計 performanceAnalysis.totalComplexity += analysis.complexity; // 性能分類 if (analysis.complexity > 10000) { performanceAnalysis.highPerformanceDetectors.push({ name: detector.name, complexity: analysis.complexity, estimatedMemory: optimization.performance.estimatedMemory.total }); } else if (analysis.complexity < 100) { performanceAnalysis.lowPerformanceDetectors.push({ name: detector.name, complexity: analysis.complexity }); } // メモリ使用量統計 performanceAnalysis.memoryEstimate.total += optimization.performance.estimatedMemory.total; performanceAnalysis.memoryEstimate.breakdown.push({ detector: detector.name, memory: optimization.performance.estimatedMemory.total }); } performanceAnalysis.averageComplexity = performanceAnalysis.totalComplexity / detectors.length; // 処理推奨事項の生成 if (performanceAnalysis.memoryEstimate.total > 1000) { performanceAnalysis.processingRecommendations.push( '高メモリ使用量が予想されます。分散処理または解像度の調整を検討してください。' ); } if (performanceAnalysis.highPerformanceDetectors.length > 5) { performanceAnalysis.processingRecommendations.push( '多数の高性能検出器があります。並列処理またはクラスター計算を推奨します。' ); } logger.info('システムDetector性能分析を完了しました', performanceAnalysis); return performanceAnalysis; } catch (error) { logger.error('システムDetector性能分析エラー', { error: error.message }); throw error; } } /** * Transformの完全性チェック * システム全体でTransform参照の整合性を確認 */ // Unit操作 - 4キー完全性保証機能強化版 async proposeUnit(length, angle, density, radioactivity) { try { // 4キー完全構造の構築 const unitData = { length, angle, density, radioactivity }; // UnitValidatorによる4キー完全性検証 UnitValidator.validateFourKeyCompleteness(unitData); // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('unit', 'unit', (n) => this.data.unit ? 'unit' : null); // 物理的整合性検証 const physicalConsistency = UnitValidator.validatePhysicalConsistency(unitData); // データ正規化 const normalizedUnit = UnitValidator.normalizeUnitStructure(unitData); await this.dataManager.addPendingChange({ action: 'proposeUnit', data: normalizedUnit }); logger.info('4キー完全単位設定を提案しました', { unitData: normalizedUnit, physicalConsistency: physicalConsistency.isConsistent, warnings: physicalConsistency.warnings.length }); let responseMessage = '提案: 単位設定を追加(4キー完全性保証)'; if (physicalConsistency.warnings.length > 0) { const warningTypes = physicalConsistency.warnings.map(w => w.type); responseMessage += ` - 警告: ${warningTypes.join(', ')}`; } return responseMessage; } catch (error) { logger.error('単位設定提案エラー', { unitData: { length, angle, density, radioactivity }, error: error.message }); throw error; } } async getUnit() { try { if (!this.data.unit) { throw new ValidationError('単位設定が存在しません', 'unit', null); } // 4キー完全性の事後検証 UnitValidator.validateFourKeyCompleteness(this.data.unit); logger.info('4キー完全単位設定を取得しました', { unit: this.data.unit }); return { unit: this.data.unit, integrity: '4-key-complete', keys: Object.keys(this.data.unit) }; } catch (error) { logger.error('単位設定取得エラー', { error: error.message }); throw error; } } /** * Transformの完全性チェック * システム全体でTransform参照の整合性を確認 */ async validateSystemTransformIntegrity() { try { const integrityResult = TransformValidator.validateTransformIntegrity(this.data); if (!integrityResult.isValid) { logger.warn('Transform参照の整合性問題が発見されました', { issues: integrityResult.issues }); } return { success: integrityResult.isValid, issues: integrityResult.issues, summary: { availableTransforms: integrityResult.availableTransforms.length, checkedReferences: integrityResult.checkedReferences, problemCount: integrityResult.issues.length } }; } catch (error) { logger.error('Transform整合性チェックエラー', { error: error.message }); throw error; } } /** * Transform使用状況統計を取得 * システム内のTransform使用状況を分析 */ async getTransformUsageReport() { try { const usageStats = TransformValidator.getTransformUsageStats(this.data); logger.info('Transform使用状況を取得しました', { total: usageStats.totalTransforms, used: usageStats.usedTransforms, unused: usageStats.unusedTransforms.length }); return { summary: { totalTransforms: usageStats.totalTransforms, usedTransforms: usageStats.usedTransforms, unusedTransforms: usageStats.unusedTransforms.length, usageEfficiency: usageStats.totalTransforms > 0 ? (usageStats.usedTransforms / usageStats.totalTransforms * 100).toFixed(1) + '%' : '0%' }, unusedTransforms: usageStats.unusedTransforms, detailedStats: usageStats.detailedStats, mostUsedTransform: usageStats.summary.mostUsed, averageUsage: usageStats.summary.averageUsage.toFixed(2) }; } catch (error) { logger.error('Transform使用統計エラー', { error: error.message }); throw error; } } async updateUnit(updates) { try { // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('unit', 'unit', (n) => this.data.unit ? 'unit' : null); // 部分更新での4キー保持検証 const validationResult = UnitValidator.validatePartialUpdate(this.data.unit, updates); // 更新後の物理的整合性検証 const physicalConsistency = UnitValidator.validatePhysicalConsistency(validationResult.updatedStructure); // データ正規化 const normalizedUpdates = {}; for (const [key, value] of Object.entries(updates)) { normalizedUpdates[key] = value.trim(); UnitValidator.validateSingleUnitKey(key, normalizedUpdates[key]); } await this.dataManager.addPendingChange({ action: 'updateUnit', data: normalizedUpdates }); logger.info('4キー保持単位更新を提案しました', { updates: normalizedUpdates, preservedIntegrity: validationResult.preservedIntegrity, changedKeys: validationResult.changedKeys, physicalConsistency: physicalConsistency.isConsistent }); let responseMessage = `提案: 単位設定の更新(${validationResult.changedKeys.join(', ')})- 4キー完全性保持`; if (physicalConsistency.warnings.length > 0) { const warningTypes = physicalConsistency.warnings.map(w => w.type); responseMessage += ` - 警告: ${warningTypes.join(', ')}`; } return responseMessage; } catch (error) { logger.error('単位設定更新エラー', { updates, error: error.message }); throw error; } } /** * 単位系の4キー完全性と物理的整合性を包括検証 */ async validateUnitIntegrity(includeSystemAnalysis = true, generateReport = true) { try { if (!this.data.unit) { throw new ValidationError('単位設定が存在しません。検証には単位設定が必要です', 'unit', null); } let report = null; if (generateReport) { report = UnitValidator.generateIntegrityDiagnosticReport( this.data.unit, includeSystemAnalysis ? this.data : null ); } // 基本4キー完全性検証 UnitValidator.validateFourKeyCompleteness(this.data.unit); // 物理的整合性検証 const physicalConsistency = UnitValidator.validatePhysicalConsistency(this.data.unit); // システム整合性検証 let systemIntegrity = null; if (includeSystemAnalysis) { systemIntegrity = UnitValidator.validateSystemUnitIntegrity(this.data.unit, this.data); } logger.info('単位系完全性検証を実行しました', { hasAllFourKeys: true, physicalConsistency: physicalConsistency.isConsistent, warningCount: physicalConsistency.warnings.length, systemAnalysis: !!systemIntegrity }); return { integrity: 'verified', fourKeyComplete: true, physicalConsistency, systemIntegrity, diagnosticReport: report, summary: { overallHealth: report ? report.overallHealth : 'verified', criticalIssues: report ? report.criticalIssues.length : 0, warnings: physicalConsistency.warnings.length, recommendations: report ? report.recommendations.length : 0 } }; } catch (error) { logger.error('単位系完全性検証エラー', { error: error.message }); throw error; } } /** * 異なる単位系間の変換係数分析 */ async analyzeUnitConversion(targetUnits, includePhysicalAnalysis = true) { try { if (!this.data.unit) { throw new ValidationError('現在の単位設定が存在しません', 'unit', null); } // 現在の単位系の4キー完全性検証 UnitValidator.validateFourKeyCompleteness(this.data.unit); // 目標単位系の4キー完全性検証 UnitValidator.validateFourKeyCompleteness(targetUnits); // 変換係数の計算 const conversionAnalysis = UnitValidator.calculateConversionFactors( this.data.unit, targetUnits ); let physicalAnalysis = null; if (includePhysicalAnalysis) { // 両方の単位系の物理的整合性分析 const currentConsistency = UnitValidator.validatePhysicalConsistency(this.data.unit); const targetConsistency = UnitValidator.validatePhysicalConsistency(targetUnits); physicalAnalysis = { current: currentConsistency, target: targetConsistency, conversionImpact: this.assessConversionImpact(conversionAnalysis) }; } logger.info('単位変換分析を実行しました', { fromUnits: this.data.unit, toUnits: targetUnits, isIdentityConversion: conversionAnalysis.isIdentity, hasPhysicalAnalysis: !!physicalAnalysis }); return { conversion: conversionAnalysis, physicalAnalysis, recommendations: this.generateConversionRecommendations(conversionAnalysis, physicalAnalysis) }; } catch (error) { logger.error('単位変換分析エラー', { targetUnits, error: error.message }); throw error; } } /** * 変換の影響評価 * @private */ assessConversionImpact(conversionAnalysis) { const impacts = []; for (const [key, factor] of Object.entries(conversionAnalysis.factors)) { if (Math.abs(factor - 1.0) > 1e-10) { let impact = 'moderate'; if (Math.abs(Math.log10(Math.abs(factor))) > 2) { impact = 'significant'; // 100倍以上の差 } else if (Math.abs(Math.log10(Math.abs(factor))) < 0.1) { impact = 'minor'; // 1.26倍未満の差 } impacts.push({ key, factor, impact, description: `${key} units require ${factor.toExponential(3)} multiplication factor` }); } } return impacts; } /** * 変換推奨事項の生成 * @private */ generateConversionRecommendations(conversionAnalysis, physicalAnalysis) { const recommendations = []; if (conversionAnalysis.isIdentity) { recommendations.push({ type: 'no_conversion_needed', priority: 'info', message: 'No unit conversion required - units are identical' }); return recommendations; } // 大きな変換係数の警告 const significantConversions = conversionAnalysis.factors; for (const [key, factor] of Object.entries(significantConversions)) { if (Math.abs(Math.log10(Math.abs(factor))) > 3) { // 1000倍以上 recommendations.push({ type: 'large_conversion_factor', priority: 'high', message: `Very large conversion factor for ${key}: ${factor.toExponential(2)}. Verify calculation accuracy.` }); } } // 物理的整合性の比較 if (physicalAnalysis) { if (physicalAnalysis.current.warnings.length < physicalAnalysis.target.warnings.length) { recommendations.push({ type: 'target_system_warnings', priority: 'medium', message: 'Target unit system has more warnings than current system. Consider physical consistency.' }); } } return recommendations; } async initialize() { // ConfigManager初期化 try { await this.dataManager.initialize(); logger.info('TaskManagerを初期化しました'); } catch (error) { // ConfigManagerが見つからない場合は警告のみ logger.warn('ConfigManagerの初期化をスキップしました', { error: error.message }); await this.dataManager.initialize(); logger.info('TaskManagerを初期化しました(ConfigManagerなし)'); } } // データアクセサ get data() { return this.dataManager.data; } get pendingChanges() { return this.dataManager.pendingChanges; } // ヘルパーメソッド findBodyByName(name) { return this.data.body?.find(b => b.name === name); } /** * propose系メソッド用の前提条件チェック(新規作成専用) * マニフェスト仕様準拠: オブジェクトが既に存在する場合は専用エラーで拒否 */ validateProposalPrerequisites(objectType, name, existingCheckMethod) { const exists = existingCheckMethod(name); if (exists) { const errorCodes = { 'body': -32064, 'zone': -32060, 'buildup_factor': -32070, 'transform': -32074, 'source': -32078, 'detector': -32082, 'unit': -32086 }; const objectTypeDisplayName = { 'body': '立体', 'zone': 'ゾーン', 'buildup_factor': 'ビルドアップ係数', 'transform': '変換', 'source': '線源', 'detector': '検出器', 'unit': '単位設定' }; const methodSuffix = objectType.charAt(0).toUpperCase() + objectType.slice(1); throw new PokerMcpError( errorCodes[objectType], `${objectTypeDisplayName[objectType]}'${name}'は既に存在します。更新する場合はupdate${methodSuffix}メソッドを使用してください`, { name, suggestion: `update${methodSuffix}`, objectType: objectTypeDisplayName[objectType] } ); } } /** * update系メソッド用の前提条件チェック(既存更新専用) * マニフェスト仕様準拠: オブジェクトが存在しない場合は専用エラーで拒否 */ validateUpdatePrerequisites(objectType, name, existingCheckMethod) { const exists = existingCheckMethod(name); if (!exists) { const errorCodes = { 'body': -32065, 'zone': -32061, 'buildup_factor': -32071, 'transform': -32075, 'source': -32079, 'detector': -32083, 'unit': -32087 }; const objectTypeDisplayName = { 'body': '立体', 'zone': 'ゾーン', 'buildup_factor': 'ビルドアップ係数', 'transform': '変換', 'source': '線源', 'detector': '検出器', 'unit': '単位設定' }; const methodSuffix = objectType.charAt(0).toUpperCase() + objectType.slice(1); throw new PokerMcpError( errorCodes[objectType], `${objectTypeDisplayName[objectType]}'${name}'が存在しません。新規作成する場合はpropose${methodSuffix}メソッドを使用してください`, { name, suggestion: `propose${methodSuffix}`, objectType: objectTypeDisplayName[objectType] } ); } } // 全立体の取得(pending changes含む) getAllBodies() { const currentBodies = this.data.body || []; const pendingBodies = this.dataManager.getPendingBodies() || []; return [...currentBodies, ...pendingBodies]; } findZoneByBodyName(bodyName) { return this.data.zone?.find(z => z.body_name === bodyName); } findSourceByName(name) { return this.data.source?.find(s => s.name === name); } findDetectorByName(name) { return this.data.detector?.find(d => d.name === name); } findBuildupFactorByMaterial(material) { return this.data.buildup_factor?.find(bf => bf.material === material); } findTransformByName(name) { return this.data.transform?.find(t => t.name === name); } validatePosition(position) { if (typeof position !== 'string' || position.trim() === '') { throw new ValidationError('positionは文字列で指定してください', 'position', position); } const coords = position.trim().split(/\s+/); if (coords.length !== 3) { throw new ValidationError('positionは3つの座標値(x y z)で指定してください', 'position', position); } for (const coord of coords) { if (isNaN(Number(coord))) { throw new ValidationError('positionの座標値は数値で指定してください', 'position', position); } } } validateBodyName(name) { ManifestValidator.validateObjectName(name, 'body name'); } // Sourceデータの正規化(座標正規化とgeometry/division処理) normalizeSourceData(sourceData) { const normalized = { ...sourceData }; logger.info('Sourceデータ正規化開始', { name: sourceData.name, type: sourceData.type, hasGeometry: !!sourceData.geometry, hasDivision: !!sourceData.division, hasPosition: !!sourceData.position }); // POINT線源の場合 if (sourceData.type === 'POINT') { if (sourceData.position) { normalized.position = this.normalizeCoordinates(sourceData.position); logger.info('POINT線源position正規化', { position: normalized.position }); } // POINT線源ではgeometryとdivisionを除去 delete normalized.geometry; delete normalized.division; logger.info('POINT線源のgeometry/divisionを除去'); return normalized; } // 体積線源のgeometry正規化 if (sourceData.geometry) { normalized.geometry = this.normalizeGeometryData(sourceData.geometry); logger.info('体積線源geometry正規化完了', { geometry: normalized.geometry }); } else { logger.warn('体積線源にgeometryがありません', { name: sourceData.name, type: sourceData.type }); } // divisionは正規化不要(数値データのみ) if (sourceData.division) { normalized.division = sourceData.division; logger.info('体積線源division保持', { division: normalized.division }); } else { logger.warn('体積線源にdivisionがありません', { name: sourceData.name, type: sourceData.type }); } // cutoff_rateは必須パラメータのため、デフォルト値設定を削除 // バリデーション時に必須チェックが実行される logger.info('Sourceデータ正規化完了', { name: normalized.name, type: normalized.type, hasGeometry: !!normalized.geometry, hasDivision: !!normalized.division, hasPosition: !!normalized.position, cutoff_rate: normalized.cutoff_rate }); return normalized; } // Geometryデータの正規化 normalizeGeometryData(geometry) { const normalized = { ...geometry }; // 座標関連フィールドの正規化 const coordinateFields = [ 'vertex', 'edge_1', 'edge_2', 'edge_3', // BOX用 'min', 'max', // RPP用 'center', // SPH用 'bottom_center', 'height_vector' // RCC用 ]; for (const field of coordinateFields) { if (normalized[field]) { normalized[field] = this.normalizeCoordinates(normalized[field]); } } return normalized; } normalizeCoordinates(coordString) { if (typeof coordString !== 'string') return coordString; // "x y z" 形式の文字列を数値として正規化 const coords = coordString.trim().split(/\s+/).map(Number); if (coords.length === 3 && coords.every(n => !isNaN(n))) { return coords.join(' '); } return coordString; } // Detectorデータの正規化 normalizeDetectorData(detectorData) { const normalized = { ...detectorData }; // 基準位置の正規化 if (normalized.origin) { normalized.origin = this.normalizeCoordinates(normalized.origin); } // グリッドデータの正規化 if (normalized.grid && Array.isArray(normalized.grid) && normalized.grid.length > 0) { normalized.grid = normalized.grid.map(gridItem => ({ edge: this.normalizeCoordinates(gridItem.edge), number: Number(gridItem.number) })); } else if ('grid' in normalized && (!normalized.grid || normalized.grid.length === 0)) { // 空配列または空の場合はgridプロパティ自体を削除 delete normalized.grid; } // show_path_traceは必須パラメータのため、デフォルト値設定を削除 // バリデーション時に必須チェックが実行される // transformが空文字列の場合は削除 if (!normalized.transform) { delete normalized.transform; } return normalized; } normalizeGridData(grid) { if (!Array.isArray(grid)) return grid; return grid.map(gridItem => ({ edge: this.normalizeCoordinates(gridItem.edge), number: Number(gridItem.number) })); } // 検出器バリデーション validateDetectorData(name, origin, grid) { // 名前バリデーション if (!name || typeof name !== 'string' || name.trim() === '') { throw new ValidationError('検出器名は必須です', 'name', name); } if (name.length > 50) { throw new ValidationError('検出器名は50文字以内で指定してください', 'name', name); } // 座標バリデーション this.validatePosition(origin); // グリッドバリデーション if (grid && Array.isArray(grid)) { if (grid.length > 3) { throw new ValidationError('gridの次元は3次元以下である必要があります', 'grid', grid); } for (const gridItem of grid) { if (!gridItem.edge || !gridItem.number) { throw new ValidationError('gridの各要素にはedgeとnumberが必要です', 'grid', gridItem); } this.validatePosition(gridItem.edge); if (!Number.isInteger(gridItem.number) || gridItem.number <= 0) { throw new ValidationError('gridのnumberは正の整数である必要があります', 'number', gridItem.number); } } } } // グリッドデータのバリデーション(更新用) validateGridData(grid) { if (!Array.isArray(grid)) { throw new ValidationError('gridは配列である必要があります', 'grid', grid); } if (grid.length > 3) { throw new ValidationError('gridの次元は3次元以下である必要があります', 'grid', grid); } for (const gridItem of grid) { if (!gridItem.edge || !gridItem.number) { throw new ValidationError('gridの各要素にはedgeとnumberが必要です', 'grid', gridItem); } this.validatePosition(gridItem.edge); if (!Number.isInteger(gridItem.number) || gridItem.number <= 0) { throw new ValidationError('gridのnumberは正の整数である必要があります', 'number', gridItem.number); } } } // 立体関連メソッド async proposeBody(name, type, options = {}) { try { // 入力バリデーション this.validateBodyName(name); ManifestValidator.validateBodyType(type, 'type'); // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('body', name, (n) => this.findBodyByName(n)); // CMB専用処理 if (type === 'CMB') { // 現在の立体一覧を取得(pending changesも含む) const existingBodies = this.getAllBodies(); // CMB専用バリデーション(context付き) const context = { name, existingBodies, allBodies: existingBodies }; PhysicsValidator.validateGeometry(type, options, context); } else { // 通常のバリデーション PhysicsValidator.validateGeometry(type, options); } // Transform参照チェック if (options.transform) { TransformValidator.validateContextTransformReference( options.transform, this.data, 'body', name ); } const newBody = this.createBodyObject(name, type, options); await this.dataManager.addPendingChange({ action: 'add_body', data: { body: newBody } }); logger.info('立体を提案しました', { name, type }); return `提案: 立体 ${name} を追加`; } catch (error) { logger.error('立体提案エラー', { name, type, error: error.message }); throw error; } } createBodyObject(name, type, options) { const body = { name, type }; // 型別のパラメータ設定(座標の正規化適用) switch (type) { case 'SPH': body.center = this.normalizeCoordinates(options.center); body.radius = Number(options.radius); break; case 'RCC': body.bottom_center = this.normalizeCoordinates(options.bottom_center); body.height_vector = this.normalizeCoordinates(options.height_vector); body.radius = Number(options.radius); break; case 'RPP': body.min = this.normalizeCoordinates(options.min); body.max = this.normalizeCoordinates(options.max); break; case 'BOX': body.vertex = this.normalizeCoordinates(options.vertex); body.edge_1 = this.normalizeCoordinates(options.edge_1); body.edge_2 = this.normalizeCoordinates(options.edge_2); body.edge_3 = this.normalizeCoordinates(options.edge_3); break; case 'CMB': body.expression = options.expression; break; case 'TOR': body.center = this.normalizeCoordinates(options.center); body.normal = this.normalizeCoordinates(options.normal); body.major_radius = Number(options.major_radius); body.minor_radius_horizontal = Number(options.minor_radius_horizontal); body.minor_radius_vertical = Number(options.minor_radius_vertical); break; case 'ELL': body.center = this.normalizeCoordinates(options.center); body.radius_vector_1 = this.normalizeCoordinates(options.radius_vector_1); body.radius_vector_2 = this.normalizeCoordinates(options.radius_vector_2); body.radius_vector_3 = this.normalizeCoordinates(options.radius_vector_3); break; case 'REC': body.bottom_center = this.normalizeCoordinates(options.bottom_center); body.height_vector = this.normalizeCoordinates(options.height_vector); body.radius_vector_1 = this.normalizeCoordinates(options.radius_vector_1); body.radius_vector_2 = this.normalizeCoordinates(options.radius_vector_2); break; case 'TRC': body.bottom_center = this.normalizeCoordinates(options.bottom_center); body.height_vector = this.normalizeCoordinates(options.height_vector); body.bottom_radius = Number(options.bottom_radius); body.top_radius = Number(options.top_radius); break; case 'WED': body.vertex = this.normalizeCoordinates(options.vertex); body.width_vector = this.normalizeCoordinates(options.width_vector); body.height_vector = this.normalizeCoordinates(options.height_vector); body.depth_vector = this.normalizeCoordinates(options.depth_vector); break; } if (options.transform) { body.transform = options.transform; } return body; } async deleteBody(name) { try { const body = this.findBodyByName(name); if (!body) { throw new ValidationError(`立体 ${name} が見つかりません`, 'name', name); } // 依存関係チェック const dependentZones = this.data.zone?.filter(z => z.body_name === name) || []; if (dependentZones.length > 0) { throw new PhysicsError( `立体 ${name} には依存するゾーンが存在します: ${dependentZones.map(z => z.body_name).join(', ')}`, 'DEPENDENT_ZONES_EXIST' ); } await this.dataManager.addPendingChange({ action: 'delete_body', data: { name: name } }); logger.info('立体削除を提案しました', { name }); return `提案: 立体 ${name} を削除`; } catch (error) { logger.error('立体削除エラー', { name, error: error.message }); throw error; } } // ゾーン関連メソッド async proposeZone(body_name, material, density) { try { // 既存のバリデーション PhysicsValidator.validateMaterialDensity(material, density); // ゾーン新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('zone', body_name, (n) => this.findZoneByBodyName(n)); // 立体存在チェック(ATMOSPHERE除く) if (body_name !== 'ATMOSPHERE') { const body = this.findBodyByName(body_name); if (!body) { // フェーズ1: 保留中の立体をチェック(同期的に呼び出し) const pendingBodies = this.checkPendingBodies(); logger.info('保留中の立体チェック', { body_name, pendingBodies }); if (pendingBodies.includes(body_name)) { throw PokerMcpError.bodyInPendingChanges(body_name); } throw new ValidationError( `立体 ${body_name} が存在しません。先にproposeBodyで立体を定義してください`, 'body_name', body_name ); } } // 材料代替機能付きバリデーション try { MaterialAlternatives.validateMaterialWithSuggestion(material, 'material'); } catch (error) { // MaterialAlternativesからのエラーをValidationErrorに変換 throw new ValidationError(error.message, 'material', material); } const newZone = { body_name, material }; if (material !== 'VOID') { newZone.density = Number(density); } await this.dataManager.addPendingChange({ action: 'add_zone', data: { zone: newZone } }); logger.info('ゾーンを提案しました', { body_name, material }); return `提案: ゾーン ${body_name} (${material}) を追加`; } catch (error) { logger.error('ゾーン提案エラー', { body_name, material, error: error.message }); throw error; } } async deleteZone(body_name) { try { if (body_name === 'ATMOSPHERE') { throw new ValidationError('ATMOSPHEREゾーンは削除できません', 'body_name', body_name); } const zone = this.findZoneByBodyName(body_name); if (!zone) { throw new ValidationError(`ゾーン ${body_name} が見つかりません`, 'body_name', body_name); } await this.dataManager.addPendingChange({ action: 'delete_zone', data: { body_name: body_name } }); logger.info('ゾーン削除を提案しました', { body_name }); return `提案: ゾーン ${body_name} を削除`; } catch (error) { logger.error('ゾーン削除エラー', { body_name, error: error.message }); throw error; } } async applyChanges() { try { const result = await this.dataManager.applyChanges(); logger.info('変更を適用しました'); return result; } catch (error) { logger.error('変更適用エラー', { error: error.message }); throw error; } } // Transform操作 async proposeTransform(name, operations) { try { if (!name) throw new ValidationError('回転移動のnameは必須です', 'name', name); if (!Array.isArray(operations) || operations.length === 0) { throw new ValidationError('operationは配列で1つ以上必要です', 'operation', operations); } // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('transform', name, (n) => this.findTransformByName(n)); // 基本バリデーション TransformValidator.validateTransformOperations(operations); // 物理的妥当性チェック const physicsAnalysis = TransformValidator.validateTransformPhysics(operations, name); await this.dataManager.addPendingChange({ action: 'proposeTransform', data: { name, operations } }); logger.info('変換提案を追加しました', { name, operations, analysis: physicsAnalysis }); return `回転移動 ${name} を追加しました - 操作数: ${physicsAnalysis.operationCount}, 回転: ${physicsAnalysis.hasRotation}, 移動: ${physicsAnalysis.hasTranslation}`; } catch (error) { logger.error('変換提案エラー', { name, error: error.message }); throw error; } } async updateTransform(name, updates) { try { if (!name) throw new ValidationError('変換のnameは必須です', 'name', name); // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('transform', name, (n) => this.findTransformByName(n)); await this.dataManager.addPendingChange({ action: 'updateTransform', data: { name, ...updates } }); logger.info('変換更新を提案しました', { name, updates }); return `提案: 変換 ${name} の更新を保留しました: ${JSON.stringify(updates)}`; } catch (error) { logger.error('変換更新エラー', { name, error: error.message }); throw error; } } async deleteTransform(name) { try { if (!name) throw PokerMcpError.validationError('変換のnameは必須です', 'name', name); // Transform名の検証 ManifestValidator.validateObjectName(name, 'transform name'); // 依存関係チェック(保留中変更も含む) TransformValidator.checkTransformDependencies(name, this.data, this.pendingChanges); await this.dataManager.addPendingChange({ action: 'deleteTransform', data: { name } }); logger.info('変換削除を提案しました', { name }); return `提案: 変換 ${name} の削除を保留しました`; } catch (error) { logger.error('変換削除エラー', { name, error: error.message }); throw error; } } // Buildup Factor操作 async proposeBuildupFactor(material, useSlantCorrection, useFiniteMediumCorrection) { try { if (!material) throw new ValidationError('materialは必須です', 'material', material); // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('buildup_factor', material, (m) => this.findBuildupFactorByMaterial(m)); // 材料代替機能付きバリデーション try { MaterialAlternatives.validateMaterialWithSuggestion(material, 'material'); } catch (error) { // MaterialAlternativesからのエラーをValidationErrorに変換 throw new ValidationError(error.message, 'material', material); } await this.dataManager.addPendingChange({ action: 'proposeBuildupFactor', data: { material, use_slant_correction: useSlantCorrection, use_finite_medium_correction: useFiniteMediumCorrection } }); logger.info('ビルドアップ係数提案を追加しました', { material }); return `ビルドアップ係数 ${material} を追加しました`; } catch (error) { logger.error('ビルドアップ係数提案エラー', { material, error: error.message }); throw error; } } async updateBuildupFactor(material, updates) { try { if (!material) throw new ValidationError('materialは必須です', 'material', material); // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('buildup_factor', material, (m) => this.findBuildupFactorByMaterial(m)); await this.dataManager.addPendingChange({ action: 'updateBuildupFactor', data: { material, ...updates } }); logger.info('ビルドアップ係数更新を提案しました', { material, updates }); return `提案: ビルドアップ係数 ${material} の更新を保留しました`; } catch (error) { logger.error('ビルドアップ係数更新エラー', { material, error: error.message }); throw error; } } async deleteBuildupFactor(material) { try { if (!material) throw new ValidationError('materialは必須です', 'material', material); await this.dataManager.addPendingChange({ action: 'deleteBuildupFactor', data: { material } }); logger.info('ビルドアップ係数削除を提案しました', { material }); return `提案: ビルドアップ係数 ${material} の削除を保留しました`; } catch (error) { logger.error('ビルドアップ係数削除エラー', { material, error: error.message }); throw error; } } async changeOrderBuildupFactor(material, newIndex) { try { if (!material) throw new ValidationError('materialは必須です', 'material', material); await this.dataManager.addPendingChange({ action: 'changeOrderBuildupFactor', data: { material, newIndex } }); logger.info('ビルドアップ係数順序変更を提案しました', { material, newIndex }); return `提案: ビルドアップ係数 ${material} の順序変更を保留しました`; } catch (error) { logger.error('ビルドアップ係数順序変更エラー', { material, error: error.message }); throw error; } } // Source操作 async proposeSource(params) { try { const { name, type, position, geometry, division, inventory, cutoff_rate } = params; logger.info('proposeSource開始', { name, type, hasPosition: !!position, hasGeometry: !!geometry, hasDivision: !!division, hasInventory: !!inventory, cutoff_rate }); // 基本バリデーション if (!name) throw PokerMcpError.validationError('線源のnameは必須です', 'name', name); ManifestValidator.validateObjectName(name, 'source name'); // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('source', name, (n) => this.findSourceByName(n)); if (!type) throw PokerMcpError.validationError('線源のtypeは必須です', 'type', type); if (!inventory) throw PokerMcpError.validationError('線源のinventoryは必須です', 'inventory', inventory); // Source構造の包括的検証(SourceValidatorを使用) const validationResult = SourceValidator.validateCompleteSourceStructure(params); logger.info('SourceValidator検証結果', validationResult); // Transform参照チェック(geometry内にある場合) if (geometry && geometry.transform) { TransformValidator.validateContextTransformReference( geometry.transform, this.data, 'source', name ); } // Source構造の最適化分析 const optimization = SourceValidator.analyzeSrcStructureOptimization(params); if (optimization.suggestions.length > 0) { logger.info('Source構造最適化の提案', { name, suggestions: optimization.suggestions }); } // データ正規化の前にパラメーター確認 logger.info('正規化前のパラメーター', { name, type, position, geometryExists: !!geometry, divisionExists: !!division, geometryContent: geometry ? JSON.stringify(geometry) : 'null', divisionContent: division ? JSON.stringify(division) : 'null' }); // データ正規化 const normalizedParams = this.normalizeSourceData(params); logger.info('正規化後のパラメーター', { name: normalizedParams.name, type: normalizedParams.type, geometryExists: !!normalizedParams.geometry, divisionExists: !!normalizedParams.division, geometryContent: normalizedParams.geometry ? JSON.stringify(normalizedParams.geometry) : 'null', divisionContent: normalizedParams.division ? JSON.stringify(normalizedParams.division) : 'null' }); await this.dataManager.addPendingChange({ action: 'proposeSource', data: normalizedParams }); logger.info('線源提案を追加しました', { name, type, validation: validationResult, optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions', finalHasGeometry: !!normalizedParams.geometry, finalHasDivision: !!normalizedParams.division }); const complexityInfo = validationResult.divisionComplexity > 1 ? ` (分割複雑度: ${validationResult.divisionComplexity})` : ''; return `提案: 線源 ${name} (${type}${complexityInfo}) を追加`; } catch (error) { logger.error('線源提案エラー', { params, error: error.message }); throw error; } } async updateSource(name, updates) { try { if (!name) throw new ValidationError('線源のnameは必須です', 'name', name); // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('source', name, (n) => this.findSourceByName(n)); // 既存線源の存在確認(詳細情報取得のため) const existingSource = this.findSourceByName(name); // 更新内容のバリデーションと正規化 const normalizedUpdates = { ...updates }; // POINT線源のposition更新 if (normalizedUpdates.position) { this.validatePosition(normalizedUpdates.position); normalizedUpdates.position = this.normalizeCoordinates(normalizedUpdates.position); } // インベントリの更新 if (normalizedUpdates.inventory) { NuclideValidator.validateInventory(normalizedUpdates.inventory); } // geometryの更新 if (normalizedUpdates.geometry) { // 既存Sourceのtypeを取得してgeometry検証 const sourceType = existingSource.type; SourceValidator.validateSourceGeometry(sourceType, normalizedUpdates.geometry); normalizedUpdates.geometry = this.normalizeGeometryData(normalizedUpdates.geometry); // Transform参照チェック if (normalizedUpdates.geometry.transform) { TransformValidator.validateContextTransformReference( normalizedUpdates.geometry.transform, this.data, 'source', name ); } } // divisionの更新 if (normalizedUpdates.division) { const sourceType = existingSource.type; SourceValidator.validateSourceDivision(sourceType, normalizedUpdates.division); } // cutoff_rateの更新 if (normalizedUpdates.cutoff_rate !== undefined) { SourceValidator.validateCutoffRate(normalizedUpdates.cutoff_rate); } // typeの変更は禁止(物理的整合性のため) if (normalizedUpdates.type && normalizedUpdates.type !== existingSource.type) { throw new ValidationError('線源のtypeは変更できません', 'type', normalizedUpdates.type); } // 更新後の構造最適化分析 const mergedSource = { ...existingSource, ...normalizedUpdates }; const optimization = SourceValidator.analyzeSrcStructureOptimization(mergedSource); if (optimization.suggestions.length > 0) { logger.info('Source更新後の最適化提案', { name, suggestions: optimization.suggestions }); } await this.dataManager.addPendingChange({ action: 'updateSource', data: { name, ...normalizedUpdates } }); logger.info('線源更新を提案しました', { name, updates: normalizedUpdates }); return `提案: 線源 ${name} の更新を保留しました`; } catch (error) { logger.error('線源更新エラー', { name, error: error.message }); throw error; } } async deleteSource(name) { try { if (!name) throw new ValidationError('線源のnameは必須です', 'name', name); // 既存線源の存在確認 const existingSource = this.findSourceByName(name); if (!existingSource) { throw new ValidationError(`線源 ${name} が見つかりません`, 'name', name); } // 依存関係チェック(将来の拡張用) // 現在は線源を直接参照する他要素はないが、将来の機能追加に備える await this.dataManager.addPendingChange({ action: 'deleteSource', data: { name } }); logger.info('線源削除を提案しました', { name }); return `提案: 線源 ${name} を削除`; } catch (error) { logger.error('線源削除エラー', { name, error: error.message }); throw error; } } // Body操作の追加メソッド async updateBody(name, updates) { try { if (!name) throw new ValidationError('立体のnameは必須です', 'name', name); // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('body', name, (n) => this.findBodyByName(n)); // 座標データの正規化 const normalizedUpdates = { ...updates }; if (normalizedUpdates.min) normalizedUpdates.min = this.normalizeCoordinates(normalizedUpdates.min); if (normalizedUpdates.max) normalizedUpdates.max = this.normalizeCoordinates(normalizedUpdates.max); if (normalizedUpdates.center) normalizedUpdates.center = this.normalizeCoordinates(normalizedUpdates.center); if (normalizedUpdates.bottom_center) normalizedUpdates.bottom_center = this.normalizeCoordinates(normalizedUpdates.bottom_center); if (normalizedUpdates.height_vector) normalizedUpdates.height_vector = this.normalizeCoordinates(normalizedUpdates.height_vector); if (normalizedUpdates.vertex) normalizedUpdates.vertex = this.normalizeCoordinates(normalizedUpdates.vertex); if (normalizedUpdates.edge_1) normalizedUpdates.edge_1 = this.normalizeCoordinates(normalizedUpdates.edge_1); if (normalizedUpdates.edge_2) normalizedUpdates.edge_2 = this.normalizeCoordinates(normalizedUpdates.edge_2); if (normalizedUpdates.edge_3) normalizedUpdates.edge_3 = this.normalizeCoordinates(normalizedUpdates.edge_3); // 新しい立体タイプのパラメータ対応 if (normalizedUpdates.normal) normalizedUpdates.normal = this.normalizeCoordinates(normalizedUpdates.normal); if (normalizedUpdates.radius_vector_1) normalizedUpdates.radius_vector_1 = this.normalizeCoordinates(normalizedUpdates.radius_vector_1); if (normalizedUpdates.radius_vector_2) normalizedUpdates.radius_vector_2 = this.normalizeCoordinates(normalizedUpdates.radius_vector_2); if (normalizedUpdates.radius_vector_3) normalizedUpdates.radius_vector_3 = this.normalizeCoordinates(normalizedUpdates.radius_vector_3); if (normalizedUpdates.width_vector) normalizedUpdates.width_vector = this.normalizeCoordinates(normalizedUpdates.width_vector); if (normalizedUpdates.depth_vector) normalizedUpdates.depth_vector = this.normalizeCoordinates(normalizedUpdates.depth_vector); await this.dataManager.addPendingChange({ action: 'updateBody', data: { name, ...normalizedUpdates } }); logger.info('立体更新を提案しました', { name, updates: normalizedUpdates }); return `提案: 立体 ${name} の更新を保留しました: ${JSON.stringify(normalizedUpdates)}`; } catch (error) { logger.error('立体更新エラー', { name, error: error.message }); throw error; } } async updateZone(bodyName, updates) { try { if (!bodyName) throw new ValidationError('ゾーンのbody_nameは必須です', 'body_name', bodyName); // 既存ゾーン更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('zone', bodyName, (n) => this.findZoneByBodyName(n)); await this.dataManager.addPendingChange({ action: 'updateZone', data: { body_name: bodyName, ...updates } }); logger.info('ゾーン更新を提案しました', { bodyName, updates }); return `提案: ゾーン ${bodyName} の更新を保留しました: ${JSON.stringify(updates)}`; } catch (error) { logger.error('ゾーン更新エラー', { bodyName, error: error.message }); throw error; } } // Detector操作 async proposeDetector(name, origin, show_path_trace = false, options = {}) { try { const { grid, transform } = options; // 基本パラメータの構築 const detectorData = { name, origin, show_path_trace }; // 明示的に指定された場合のみプロパティを追加 if (grid !== undefined) detectorData.grid = grid; if (transform) detectorData.transform = transform; // DetectorValidatorによる包括的検証 const validationResult = DetectorValidator.validateCompleteDetectorStructure( detectorData, this.data ); // 新規作成専用チェック - マニフェスト仕様準拠 this.validateProposalPrerequisites('detector', name, (n) => this.findDetectorByName(n)); // Transform参照チェック(指定されている場合) if (transform) { DetectorValidator.validateDetectorTransform(transform, this.data, name); } // Detector最適化分析 const optimization = DetectorValidator.analyzeDetectorOptimization(detectorData, this.data); if (optimization.suggestions.length > 0) { logger.info('Detector構造最適化の提案', { name, suggestions: optimization.suggestions }); } // データ正規化 const normalizedDetector = this.normalizeDetectorData(detectorData); await this.dataManager.addPendingChange({ action: 'proposeDetector', data: normalizedDetector }); logger.info('検出器提案を追加しました', { name, validation: validationResult, optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions', performance: optimization.performance }); const complexityInfo = validationResult.complexity > 1 ? ` (複雑度: ${validationResult.complexity})` : ''; const dimensionInfo = validationResult.gridAnalysis ? ` ${validationResult.gridAnalysis.dimension}D` : ' Point'; return `提案: 検出器 ${name}${dimensionInfo}${complexityInfo} を追加`; } catch (error) { logger.error('検出器提案エラー', { name, origin, error: error.message }); throw error; } } async updateDetector(name, updates) { try { if (!name) throw new ValidationError('検出器のnameは必須です', 'name', name); // 既存更新専用チェック - マニフェスト仕様準拠 this.validateUpdatePrerequisites('detector', name, (n) => this.findDetectorByName(n)); // 既存検出器の存在確認(詳細情報取得のため) const existingDetector = this.findDetectorByName(name); // 更新データの構築 const updatedDetectorData = { ...existingDetector, ...updates, name }; // DetectorValidatorによる更新後データの検証 const validationResult = DetectorValidator.validateCompleteDetectorStructure( updatedDetectorData, this.data ); // Transform参照チェック(更新される場合) if (updates.transform !== undefined) { if (updates.transform) { DetectorValidator.validateDetectorTransform(updates.transform, this.data, name); } } // 更新後の最適化分析 const optimization = DetectorValidator.analyzeDetectorOptimization(updatedDetectorData, this.data); if (optimization.suggestions.length > 0) { logger.info('Detector更新後の最適化提案', { name, suggestions: optimization.suggestions }); } // データ正規化 const normalizedUpdates = this.normalizeDetectorData(updates); // nameフィールドは削除(名前変更不可) delete normalizedUpdates.name; await this.dataManager.addPendingChange({ action: 'updateDetector', data: { name, ...normalizedUpdates } }); logger.info('検出器更新を提案しました', { name, updates: normalizedUpdates, validation: validationResult, optimization: optimization.isOptimal ? 'optimal' : 'has_suggestions' }); const complexityChange = validationResult.complexity !== existingDetector.grid?.length ? ` (複雑度: ${validationResult.complexity})` : ''; return `提案: 検出器 ${name} の更新を保留しました${complexityChange}`; } catch (error) { logger.error('検出器更新エラー', { name, error: error.message }); throw error; } } async deleteDetector(name) { try { if (!name) throw new ValidationError('検出器のnameは必須です', 'name', name); // 既存検出器の存在確認 const existingDetector = this.findDetectorByName(name); if (!existingDetector) { throw new ValidationError(`検出器 ${name} が見つかりません`, 'name', name); } // 依存関係チェック(将来の拡張用) // 現在は検出器を直接参照する他要素はないが、将来の機能追加に備える await this.dataManager.addPendingChange({ action: 'deleteDetector', data: { name } }); logger.info('検出器削除を提案しました', { name }); return `提案: 検出器 ${name} を削除`; } catch (error) { logger.error('検出器削除エラー', { name, error: error.message }); throw error; } } /** * YAML初期化メソッド - poker_resetYaml用 * マニフェストのreset_level_definitionsに基づく段階的リセット */ async resetYaml(options) { try { const { backupComment = 'Manual reset before initialization', force = false, preserveUnits = true, resetLevel = 'standard', atmosphereMaterial = 'VOID', atmosphereDensity = undefined } = options; logger.info('YAML初期化を開始', { resetLevel, preserveUnits, atmosphereMaterial, atmosphereDensity }); // リセットレベルの検証 const validLevels = ['minimal', 'standard', 'complete']; if (!validLevels.includes(resetLevel)) { throw new ValidationError(`無効なリセットレベル: ${resetLevel}。有効な値: ${validLevels.join(', ')}`, 'resetLevel', resetLevel); } // 材料の検証 const validMaterials = [ 'Carbon', 'Concrete', 'Iron', 'Lead', 'Aluminum', 'Copper', 'Tungsten', 'Air', 'Water', 'PyrexGlass', 'AcrylicResin', 'Polyethylene', 'Soil', 'VOID' ]; if (!validMaterials.includes(atmosphereMaterial)) { throw new ValidationError(`無効な材料: ${atmosphereMaterial}。有効な値: ${validMaterials.join(', ')}`, 'atmosphereMaterial', atmosphereMaterial); } // VOID材料の密度指定チェック if (atmosphereMaterial === 'VOID' && atmosphereDensity !== undefined) { logger.warn('VOID材料では密度指定を無視します', { atmosphereDensity }); } // VOID以外の材料で密度未指定チェック if (atmosphereMaterial !== 'VOID' && atmosphereDensity === undefined) { throw new ValidationError(`材料 ${atmosphereMaterial} には密度の指定が必要です`, 'atmosphereDensity', atmosphereDensity); } // 密度の範囲チェック if (atmosphereDensity !== undefined) { if (atmosphereDensity < 0.001 || atmosphereDensity > 30.0) { throw new ValidationError(`密度は0.001から30.0の範囲で指定してください: ${atmosphereDensity}`, 'atmosphereDensity', atmosphereDensity); } } // DataManagerでリセット実行 const result = await this.dataManager.resetToInitialState({ backupComment, preserveUnits, resetLevel, atmosphereMaterial, atmosphereDensity, force }); // リセット後の追加検証 await this.validateResetResult(resetLevel); const response = { success: true, action: 'resetYaml', result: result, parameters: { resetLevel, preserveUnits, atmosphereMaterial, atmosphereDensity: atmosphereMaterial === 'VOID' ? undefined : atmosphereDensity }, timestamp: new Date().toISOString() }; logger.info('YAML初期化が正常に完了しました', response); return response; } catch (error) { logger.error('YAML初期化エラー', { error: error.message }); // エラーの種類に応じた適切なエラーコード設定 if (error instanceof ValidationError) { if (error.message.includes('無効なリセットレベル')) { error.code = 'INVALID_RESET_LEVEL'; } else if (error.message.includes('材料')) { error.code = 'ATMOSPHERE_VALIDATION_FAILED'; } } throw error; } } /** * リセット結果の検証 */ async validateResetResult(resetLevel) { try { // 基本的な構造検証 if (!this.data) { throw new ValidationError('データが読み込まれていません', 'data', null); } // 単位セクションの検証 if (!this.data.unit) { throw new ValidationError('単位セクションが存在しません', 'unit', null); } const requiredUnitKeys = ['length', 'angle', 'density', 'radioactivity']; const missingKeys = requiredUnitKeys.filter(key => !this.data.unit[key]); if (missingKeys.length > 0) { throw new ValidationError(`単位セクションに不足キー: ${missingKeys.join(', ')}`, 'unit', missingKeys); } // ATMOSPHEREゾーンの検証 if (!this.data.zone || !Array.isArray(this.data.zone)) { throw new ValidationError('ゾーンセクションが存在しません', 'zone', null); } const atmosphere = this.data.zone.find(z => z.body_name === 'ATMOSPHERE'); if (!atmosphere) { throw new ValidationError('ATMOSPHEREゾーンが存在しません', 'zone', null); } if (!atmosphere.material) { throw new ValidationError('ATMOSPHEREゾーンに材料が指定されていません', 'atmosphere', atmosphere); } // リセットレベル別の検証 switch (resetLevel) { case 'minimal': // minimalでは一部のセクションが保持されているはず logger.info('minimal リセットの検証完了'); break; case 'standard': // standardでは単位とATMOSPHEREのみ保持 if (this.data.body && this.data.body.length > 0) { logger.warn('standard リセット後に立体が残っています', { bodies: this.data.body.map(b => b.name) }); } break; case 'complete': // completeでは全て初期状態 if (this.data.body && this.data.body.length > 0) { logger.warn('complete リセット後に立体が残っています', { bodies: this.data.body.map(b => b.name) }); } break; } logger.info('リセット結果の検証が完了しました', { resetLevel }); return true; } catch (error) { logger.error('リセット結果の検証に失敗', { error: error.message }); throw new ValidationError(`リセット後検証失敗: ${error.message}`, 'postResetValidation', null); } } // フェーズ1追加: 保留中の立体をチェック(同期的に変更) checkPendingBodies() { try { // pendingChangesプロパティを使用 const pending = this.pendingChanges || []; const pendingBodies = []; for (const change of pending) { if (change.action === 'add_body') { pendingBodies.push(change.data.body.name); } } logger.info('保留中の立体を検出', { count: pendingBodies.length, bodies: pendingBodies }); return pendingBodies; } catch (error) { logger.error('保留中の立体チェックエラー', { error: error.message }); return []; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hirao-Y/poker_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server