diff --git a/Documentation/MCP/MCP.md b/Documentation/MCP/MCP.md
deleted file mode 100644
index 3c2bb52..0000000
--- a/Documentation/MCP/MCP.md
+++ /dev/null
@@ -1,170 +0,0 @@
-# CtrEditor MCP Server Documentation
-
-## 🎯 Overview
-
-The CtrEditor MCP (Model Context Protocol) Server transforms the CtrEditor WPF application into a programmable automation platform for debugging, testing, and simulation control. This server enables remote control of all major application features through JSON-RPC style communication.
-
-## 🚀 Architecture
-
-### Communication Flow
-```
-LLM/Client ↔ MCP Proxy (Python) ↔ CtrEditor MCP Server (C#) ↔ WPF Application
-```
-
-### Connection Setup
-- **Protocol**: TCP Socket on port 5006
-- **Format**: JSON-RPC style messages
-- **Proxy**: Python proxy for VS Code Copilot integration
-
-### VS Code Configuration
-```json
-"ctreditor": {
- "command": "C:/Users/migue/miniconda3/envs/mcp_proxy/python.exe",
- "args": [
- "d:/Proyectos/Scripts/MCP_Proxy/mcp_proxy.py",
- "--host", "localhost",
- "--port", "5006",
- "--name", "CtrEditor",
- "--description", "CtrEditor WPF Application MCP Server for debugging and testing"
- ]
-}
-```
-
-## 📋 Available Tools
-
-### 🔧 Object Management
-
-#### `list_objects`
-**Description**: Get complete list of all simulation objects
-**Parameters**: None
-**Returns**: Array of objects with full metadata (ID, name, type, position, properties)
-
-#### `create_object`
-**Description**: Create a new simulation object
-**Parameters**:
-- `type` (string): Object type (e.g., "osHydTank", "osHydPump")
-- `x` (number): X position in meters
-- `y` (number): Y position in meters
-
-#### `update_object`
-**Description**: Modify properties of existing object
-**Parameters**:
-- `id` (string): Object ID
-- `properties` (object): JSON object with properties to update
-
-#### `delete_objects`
-**Description**: Remove objects from simulation
-**Parameters**:
-- `ids` (array): Array of object IDs to delete
-
-#### `list_object_types`
-**Description**: Get list of all creatable object types
-**Parameters**: None
-
-### ⚙️ Simulation Control
-
-#### `start_simulation`
-**Description**: Start the physics simulation
-**Parameters**: None
-
-#### `stop_simulation`
-**Description**: Stop the physics simulation
-**Parameters**: None
-
-#### `get_simulation_status`
-**Description**: Get current simulation state
-**Parameters**: None
-**Returns**: Status info (running, object count, etc.)
-
-### 🔌 PLC Integration
-
-#### `get_plc_status`
-**Description**: Get PLC connection status
-**Parameters**: None
-**Returns**: Connection state and status information
-
-### 📸 Visual Documentation
-
-#### `take_screenshot`
-**Description**: Capture canvas screenshot with high resolution
-**Parameters** (all optional):
-- `x` (number): X position in meters for partial capture
-- `y` (number): Y position in meters for partial capture
-- `width` (number): Width in meters for partial capture
-- `height` (number): Height in meters for partial capture
-- `include_background` (boolean): Include canvas background (default: false)
-
-**Features**:
-- **High Resolution**: 2x scale for full canvas, 3x for partial captures
-- **Auto-Directory**: Saves to `/screenshots/` subdirectory
-- **Detailed Response**: File size, dimensions, timestamps, paths
-
-### 💾 Project Management
-
-#### `save_project`
-**Description**: Save current project state
-**Parameters**: None
-
-## 🎨 Key Features
-
-### 📊 Rich Object Information
-- **Complete Properties**: All object properties in JSON format
-- **Simulation Data**: Real-time physics and hydraulic states
-- **Coordinate System**: All positions in engineering meters
-- **Type Safety**: Full .NET type information preserved
-
-### 🖼️ Advanced Screenshots
-- **Meter-Based Coordinates**: Natural engineering units
-- **High Resolution**: Anti-aliased, print-quality images
-- **Flexible Areas**: Full canvas or custom rectangular regions
-- **Background Control**: With/without canvas background
-
-### 🔄 Real-Time Monitoring
-- **Live Updates**: Monitor simulation state changes
-- **Property Tracking**: Observe dynamic property changes during simulation
-- **Performance Data**: Track flow rates, pressures, speeds, etc.
-
-### 🛡️ Error Handling
-- **Detailed Errors**: Comprehensive error messages with context
-- **Safe Operations**: Thread-safe UI operations via Dispatcher
-- **Graceful Degradation**: Handles connection issues and timeouts
-
-## 🎯 Use Cases
-
-### 🔬 Simulation Debugging
-1. **Pre-Simulation**: Inspect object configurations and positions
-2. **Runtime Monitoring**: Track dynamic properties during simulation
-3. **Post-Analysis**: Compare before/after states
-4. **Visual Verification**: Screenshot specific areas for documentation
-
-### 🤖 Automated Testing
-- **Regression Testing**: Verify simulation behavior consistency
-- **Performance Benchmarking**: Monitor simulation performance metrics
-- **Configuration Validation**: Ensure proper object setup
-- **Visual Regression**: Compare screenshots across versions
-
-### 📚 Documentation Generation
-- **Auto-Documentation**: Extract object configurations automatically
-- **Visual Documentation**: Generate annotated screenshots
-- **State Reports**: Create detailed simulation state reports
-- **Training Materials**: Generate step-by-step simulation guides
-
-## 🔧 Technical Implementation
-
-### Core Components
-- **MCPServer.cs**: Main TCP server with JSON-RPC handling
-- **Tool Handlers**: Specialized handlers for each operation type
-- **Thread Safety**: UI operations via WPF Dispatcher
-- **Error Management**: Comprehensive exception handling
-
-### Performance Optimizations
-- **Lazy Connection**: Connect only when needed
-- **Efficient Serialization**: Optimized JSON generation
-- **Memory Management**: Proper resource cleanup
-- **High-Resolution Rendering**: Optimized screenshot generation
-
-### Security Considerations
-- **Local-Only**: Bound to localhost for security
-- **No File System Access**: Limited to application operations
-- **Controlled Operations**: No dangerous system operations
-- **Sandboxed**: Isolated from system configuration
diff --git a/Documentation/MCP/MCP_LLM_Guide_Optimized.md b/Documentation/MCP/MCP_LLM_Guide_Optimized.md
deleted file mode 100644
index c7d2acc..0000000
--- a/Documentation/MCP/MCP_LLM_Guide_Optimized.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# CtrEditor MCP Server - LLM Guide
-
-## ⚡ Command Efficiency Tiers
-
-### 🚀 **Ultra-Fast** (Use Liberally)
-- `get_simulation_status` → ~25 tokens, instant
-- `get_ctreditor_status` → ~20 tokens, instant
-- `start/stop_simulation` → ~15 tokens, 2-3 sec
-
-### 🟡 **Medium** (Use When Needed)
-- `search_debug_log` → 50-200 tokens (max_lines=3-10)
-- `build_project` → 100-500 tokens (errors only)
-
-### 🔴 **Heavy** (Use Sparingly)
-- `list_objects` → 500-2000+ tokens
-- `take_screenshot` → 100-300 tokens + file
-
-## 🔧 Core Operations
-
-### Process Management
-```json
-{"tool": "start_ctreditor", "parameters": {}}
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "stop_ctreditor", "parameters": {}}
-```
-
-### Build & Debug
-```json
-{"tool": "build_project", "parameters": {}}
-{"tool": "start_debug_listener", "parameters": {}}
-{"tool": "search_debug_log", "parameters": {"pattern": "error|exception", "max_lines": 5}}
-{"tool": "stop_debug_listener", "parameters": {}}
-```
-
-### Simulation Control
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-{"tool": "stop_simulation", "parameters": {}}
-```
-
-### Object Management
-```json
-{"tool": "list_objects", "parameters": {}}
-{"tool": "create_object", "parameters": {"type": "osHydPump", "x": 10, "y": 5}}
-{"tool": "update_object", "parameters": {"id": "123", "properties": {"PumpHead": 75.0}}}
-{"tool": "delete_objects", "parameters": {"ids": ["123", "456"]}}
-```
-
-## ⚡ Optimal Workflows
-
-### Quick Development Cycle
-```json
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-### Bug Investigation
-```json
-{"tool": "search_debug_log", "parameters": {"pattern": "error|exception", "max_lines": 3}}
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-### System Recovery
-```json
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "start_ctreditor", "parameters": {}}
-```
-
-## 📊 Key Object Properties
-
-### Hydraulic Components
-- **osHydTank**: `TankPressure`, `MaxLevel`, `MinLevel`, `IsFixedPressure`
-- **osHydPump**: `PumpHead`, `MaxFlow`, `SpeedRatio`, `IsRunning`
-- **osHydPipe**: `Length`, `Diameter`, `CurrentFlow`, `PressureDrop`
-
-### Common Properties
-- **Position**: `Left`, `Top` (meters)
-- **Dimensions**: `Ancho`, `Alto` (meters)
-- **Connections**: `Id_ComponenteA`, `Id_ComponenteB`
-
-## 🖼️ Screenshots
-
-### Full Canvas
-```json
-{"tool": "take_screenshot", "parameters": {}}
-```
-
-### Targeted Area
-```json
-{"tool": "take_screenshot", "parameters": {
- "x": 39.0, "y": 19.0, "width": 7.0, "height": 3.0
-}}
-```
-
-## 🔍 Debug Search Patterns
-
-High-value, low-token patterns:
-- **Critical Errors**: `"error|exception|fail"` (max_lines=3)
-- **Performance**: `"fps|slow|memory"` (max_lines=5)
-- **Simulation**: `"simulation.*error|physics.*fail"` (max_lines=3)
-
-## 🚨 Troubleshooting
-
-### Build Issues
-```json
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "clean_project", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-```
-
-### Connection Issues
-1. Use `get_simulation_status` to test connectivity
-2. Check CtrEditor is running with `get_ctreditor_status`
-3. Verify MCP server is active on port 5006
-
-## 💡 Best Practices
-
-- Always use `get_simulation_status` before expensive operations
-- Use specific patterns in `search_debug_log` with low max_lines
-- Call `list_objects` only when object data is actually needed
-- Stop simulation before major structural changes
-- Use appropriate units: meters, Pascal, m³/s
diff --git a/Documentation/MCP/MCP_LLM_Guide_Original.md b/Documentation/MCP/MCP_LLM_Guide_Original.md
deleted file mode 100644
index 22b4a8a..0000000
--- a/Documentation/MCP/MCP_LLM_Guide_Original.md
+++ /dev/null
@@ -1,789 +0,0 @@
-# CtrEditor MCP Server - LLM Guide
-
-## ⚡ Command Efficiency Tiers
-
-### 🚀 **Ultra-Fast** (Use Liberally)
-- `get_simulation_status` → ~25 tokens, instant
-- `get_ctreditor_status` → ~20 tokens, instant
-- `start/stop_simulation` → ~15 tokens, 2-3 sec
-
-### 🟡 **Medium** (Use When Needed)
-- `search_debug_log` → 50-200 tokens (max_lines=3-10)
-- `build_project` → 100-500 tokens (errors only)
-
-### 🔴 **Heavy** (Use Sparingly)
-- `list_objects` → 500-2000+ tokens
-- `take_screenshot` → 100-300 tokens + file
-
-## � Core Operations
-
-### Process Management
-```json
-{"tool": "start_ctreditor", "parameters": {}}
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "stop_ctreditor", "parameters": {}}
-```
-
-### Build & Debug
-```json
-{"tool": "build_project", "parameters": {}}
-{"tool": "start_debug_listener", "parameters": {}}
-{"tool": "search_debug_log", "parameters": {"pattern": "error|exception", "max_lines": 5}}
-{"tool": "stop_debug_listener", "parameters": {}}
-```
-
-### Simulation Control
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-{"tool": "stop_simulation", "parameters": {}}
-```
-
-### Object Management
-```json
-{"tool": "list_objects", "parameters": {}}
-{"tool": "create_object", "parameters": {"type": "osHydPump", "x": 10, "y": 5}}
-{"tool": "update_object", "parameters": {"id": "123", "properties": {"PumpHead": 75.0}}}
-{"tool": "delete_objects", "parameters": {"ids": ["123", "456"]}}
-```
-
-## ⚡ Optimal Workflows
-
-### Quick Development Cycle
-```json
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-### Bug Investigation
-```json
-{"tool": "search_debug_log", "parameters": {"pattern": "error|exception", "max_lines": 3}}
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-### System Recovery
-```json
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "start_ctreditor", "parameters": {}}
-```
-
-### 🎯 Micro-Workflows (Ultra-Efficient)
-
-#### **Instant Status Check (1-2 seconds, <50 tokens)**
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-```
-Use this before any operation - tells you everything you need to know
-
-#### **Quick Error Scan (3-5 seconds, <100 tokens)**
-```json
-{"tool": "search_debug_log", "parameters": {
- "pattern": "error|fail|exception",
- "max_lines": 3
-}}
-```
-Surgical error detection without token waste
-
-#### **Smart Object Count Verification (1 second, ~25 tokens)**
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-```
-Check object_count property instead of calling list_objects
-
-#### **Efficient Build Verification (5-10 seconds, filtered output)**
-```json
-{"tool": "build_project", "parameters": {}}
-```
-Only returns errors - ignores verbose build output
-
-### � Optimized Recovery Patterns
-
-#### **Process Recovery (Fast)**
-```json
-// 1. Check status (instant)
-{"tool": "get_ctreditor_status", "parameters": {}}
-
-// 2. If needed, restart (5-10 seconds)
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "start_ctreditor", "parameters": {}}
-```
-
-#### **Simulation Recovery (Ultra-Fast)**
-```json
-// 1. Quick reset (3 seconds total)
-{"tool": "stop_simulation", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-
-// 2. Verify (instant)
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-#### **Debug Recovery (Minimal)**
-```json
-{"tool": "stop_debug_listener", "parameters": {}}
-{"tool": "start_debug_listener", "parameters": {}}
-```
-
-### 🧹 Cleanup (Minimal Token Usage)
-```
-{"tool": "stop_simulation", "parameters": {}} // if running
-{"tool": "save_project", "parameters": {}} // if changes made
-{"tool": "stop_ctreditor", "parameters": {}} // when done
-```
-
-## 📊 Object Property Deep Dive
-
-### Hydraulic Components
-
-#### **osHydTank** (Hydraulic Tank)
-Key Properties to Monitor:
-- `TankType`: Type of tank (1=standard)
-- `CrossSectionalArea`: Tank cross-section in m²
-- `MaxLevel`, `MinLevel`: Level limits in meters
-- `TankPressure`: Pressure in Pascal
-- `IsFixedPressure`: Whether pressure is constant
-
-#### **osHydPump** (Hydraulic Pump)
-Key Properties to Monitor:
-- `PumpHead`: Pump head in meters
-- `MaxFlow`: Maximum flow rate in m³/s
-- `SpeedRatio`: Speed ratio (0.0-1.0)
-- `IsRunning`: Pump operational state
-- `PumpDirection`: Flow direction (1=forward, -1=reverse)
-
-#### **osHydPipe** (Hydraulic Pipe)
-Key Properties to Monitor:
-- `Length`: Pipe length in meters
-- `Diameter`: Internal diameter in meters
-- `Roughness`: Surface roughness
-- `CurrentFlow`: Current flow rate in m³/s
-- `PressureDrop`: Pressure loss across pipe
-- `Id_ComponenteA`, `Id_ComponenteB`: Connected components
-
-### Dynamic Properties
-During simulation, monitor these changing values:
-- Flow rates in pipes (`CurrentFlow`)
-- Pressure drops (`PressureDrop`)
-- Tank levels (if applicable)
-- Pump performance metrics
-
-## 🖼️ Screenshots
-
-### Full Canvas
-```json
-{"tool": "take_screenshot", "parameters": {}}
-```
-
-### Targeted Area
-```json
-{"tool": "take_screenshot", "parameters": {
- "x": 39.0, "y": 19.0, "width": 7.0, "height": 3.0
-}}
-```
-
-## 🔍 Debug Search Patterns
-
-High-value, low-token patterns:
-- **Critical Errors**: `"error|exception|fail"` (max_lines=3)
-- **Performance**: `"fps|slow|memory"` (max_lines=5)
-- **Simulation**: `"simulation.*error|physics.*fail"` (max_lines=3)
-
-## � Troubleshooting
-
-### Build Issues
-```json
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "clean_project", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-```
-
-### Connection Issues
-1. Use `get_simulation_status` to test connectivity
-2. Check CtrEditor is running with `get_ctreditor_status`
-3. Verify MCP server is active on port 5006
-
-## 💡 Best Practices
-
-- Always use `get_simulation_status` before expensive operations
-- Use specific patterns in `search_debug_log` with low max_lines
-- Call `list_objects` only when object data is actually needed
-- Stop simulation before major structural changes
-- Use appropriate units: meters, Pascal, m³/s
- "pattern": "error|exception",
- "max_lines": 20
-}}
-
-// Check buffer stats
-{"tool": "get_debug_stats", "parameters": {}}
-
-// Stop monitoring
-{"tool": "stop_debug_listener", "parameters": {}}
-```
-
-### Combined Development Workflow
-```json
-// 1. Ensure clean environment
-{"tool": "get_ctreditor_status", "parameters": {}}
-{"tool": "stop_ctreditor", "parameters": {}}
-{"tool": "clean_project", "parameters": {}}
-
-// 2. Build and check for errors
-{"tool": "build_project", "parameters": {}}
-
-// 3. Start CtrEditor with monitoring
-{"tool": "start_debug_listener", "parameters": {}}
-{"tool": "start_ctreditor", "parameters": {}}
-
-// 4. Monitor for issues
-{"tool": "search_debug_log", "parameters": {
- "pattern": "error|exception|fail",
- "max_lines": 10
-}}
-
-// 5. Test simulation
-{"tool": "get_simulation_status", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-
-// 6. Search for simulation issues
-{"tool": "search_debug_log", "parameters": {
- "pattern": "simulation|physics",
- "max_lines": 15
-}}
-```
-
-## 🔧 Enhanced Debugging Workflows (NEW)
-
-### 1. Build Error Investigation
-```
-1. build_project - Get compilation errors only (filtered)
-2. search_debug_log(pattern="error|warning", max_lines=50) - Find related debug info
-3. start_ctreditor - Launch for testing
-4. get_debug_stats - Monitor for new issues
-```
-
-### 2. Runtime Issue Investigation
-```
-1. start_debug_listener - Begin monitoring
-2. start_simulation - Trigger the issue
-3. search_debug_log(pattern="exception|error", last_n_lines=200) - Find problems
-4. search_debug_log(pattern="stack trace", max_lines=10) - Get stack traces
-5. stop_simulation - Stop for analysis
-```
-
-### 3. Performance Analysis
-```
-1. clear_debug_buffer - Start with clean slate
-2. start_simulation - Begin performance test
-3. search_debug_log(pattern="fps|performance|slow", max_lines=20) - Find performance issues
-4. search_debug_log(pattern="memory|allocation", max_lines=15) - Check memory usage
-5. get_debug_stats - Overall statistics
-```
-
-### 4. Component Behavior Analysis
-```
-1. search_debug_log(pattern="pump|flow|pressure", max_lines=30) - Hydraulic behavior
-2. search_debug_log(pattern="simulation.*object", max_lines=25) - Object interactions
-3. take_screenshot() - Visual verification
-4. list_objects - Current property values
-```
-
-### 5. Pre-Simulation Analysis
-```
-1. list_objects - Document initial state
-2. take_screenshot() - Visual documentation
-3. Analyze object configurations and connections
-4. Verify proper component setup
-```
-
-### 6. Simulation Monitoring
-```
-1. start_simulation - Begin simulation
-2. get_simulation_status - Confirm running
-3. list_objects - Monitor dynamic properties
-4. take_screenshot(area) - Capture specific components
-5. stop_simulation - End when needed
-```
-
-### 7. Performance Investigation
-```
-1. list_objects - Get baseline measurements
-2. start_simulation - Run simulation
-3. Wait 5-10 seconds for stabilization
-4. list_objects - Compare with baseline
-5. take_screenshot() - Document final state
-```
-
-### 8. Component Analysis
-```
-1. take_screenshot(component_area) - High-res component view
-2. list_objects - Get detailed properties
-3. update_object - Modify parameters if needed
-4. start_simulation - Test changes
-5. take_screenshot(component_area) - Compare results
-```
-
-## 📈 Simulation Data Interpretation
-
-### Flow Analysis
-- **Positive Flow**: Normal direction flow
-- **Negative Flow**: Reverse flow direction
-- **Zero Flow**: No flow (blockage or equilibrium)
-
-### Pressure Analysis
-- **High Pressure Drop**: Potential flow restriction
-- **Low Pressure Drop**: Free-flowing condition
-- **Pressure Patterns**: Indicate system behavior
-
-### Pump Performance
-- **SpeedRatio**: Pump operation intensity
-- **IsRunning**: Operational state
-- **PumpDirection**: Flow direction control
-
-## ⚡ Rapid Iteration Patterns (NEW)
-
-### Lightning Fast Debug Cycle
-**Single-Command Quick Check (5 seconds):**
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-```
-- Instantly know: running state, object count, visibility
-- Use this before any other operation
-
-### Minimal Token Development Loop
-**Ultra-Efficient 3-Step Pattern:**
-```json
-// 1. Quick status (minimal tokens)
-{"tool": "get_ctreditor_status", "parameters": {}}
-
-// 2. Smart build (errors only, no full log)
-{"tool": "build_project", "parameters": {}}
-
-// 3. Target-specific debug search (specific pattern)
-{"tool": "search_debug_log", "parameters": {
- "pattern": "error|exception",
- "max_lines": 5
-}}
-```
-
-### Zero-Waste Object Analysis
-**Smart Object Inspection (instead of full list_objects):**
-```json
-// Instead of listing all objects, target specific ones:
-{"tool": "list_objects", "parameters": {}}
-// Then filter client-side by type/name/properties
-```
-
-### Micro-Screenshot Strategy
-**Targeted Visual Validation (saves bandwidth):**
-```json
-// Full canvas only when needed
-{"tool": "take_screenshot", "parameters": {}}
-
-// For specific components (higher detail, smaller file):
-{"tool": "take_screenshot", "parameters": {
- "x": 42.0, "y": 20.0, "width": 2.0, "height": 2.0
-}}
-```
-
-## � Token-Optimized Workflows
-
-### Development Iteration (Minimal Tokens)
-```
-1. get_ctreditor_status (1 call - instant)
-2. build_project (errors only - no verbose output)
-3. start_simulation (1 call - instant feedback)
-4. get_simulation_status (verify running)
-```
-
-### Bug Investigation (Surgical Precision)
-```
-1. search_debug_log(pattern="specific_error", max_lines=3)
-2. list_objects (only if object data needed)
-3. take_screenshot (targeted area only)
-```
-
-### Performance Testing (Streamlined)
-```
-1. clear_debug_buffer (clean slate)
-2. start_simulation
-3. search_debug_log(pattern="fps|performance", max_lines=10)
-4. stop_simulation
-```
-
-## 🎯 Smart Command Selection Guide
-
-### When to Use Each Function:
-- **get_ctreditor_status**: Always first command (2 tokens response)
-- **build_project**: Only when code changed (filtered output)
-- **search_debug_log**: Instead of full console dump (targeted)
-- **get_simulation_status**: Quick health check (3 tokens response)
-- **list_objects**: Only when need object data (can be large)
-- **take_screenshot**: Last resort for visual confirmation
-
-### Command Efficiency Ranking:
-1. 🟢 **Ultra-Fast** (use liberally): `get_ctreditor_status`, `get_simulation_status`
-2. 🟡 **Fast** (use when needed): `start/stop_simulation`, `search_debug_log`
-3. 🟠 **Medium** (use selectively): `build_project`, `save_project`
-4. 🔴 **Heavy** (use sparingly): `list_objects`, `take_screenshot`
-
-## �🚨 Enhanced Troubleshooting Guide (NEW)
-
-### Process Management Issues
-If CtrEditor won't start or crashes:
-1. `get_ctreditor_status` - Check current process state (instant)
-2. `stop_ctreditor` - Ensure clean termination
-3. `build_project` - Check for compilation issues (errors only)
-4. `start_ctreditor` - Launch fresh instance
-5. `search_debug_log` - Monitor for startup issues (targeted pattern)
-
-### Build Problems (Smart Filtering)
-Critical Build Error Patterns:
-- **CS#### errors**: Compilation issues (fix immediately)
-- **MSB3027/MSB3021**: File lock errors (CtrEditor running)
-- **MSB#### warnings**: Build system (usually safe to ignore)
-
-Quick Build Fix Flow:
-```json
-// 1. Check if app is blocking compilation
-{"tool": "get_ctreditor_status", "parameters": {}}
-
-// 2. Stop if running
-{"tool": "stop_ctreditor", "parameters": {}}
-
-// 3. Clean and rebuild
-{"tool": "clean_project", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-```
-
-### Debug Console Issues (Connection Problems)
-Fast Debug Recovery:
-```json
-// 1. Check connection health
-{"tool": "get_debug_stats", "parameters": {}}
-
-// 2. Reset connection
-{"tool": "stop_debug_listener", "parameters": {}}
-{"tool": "start_debug_listener", "parameters": {}}
-```
-
-### Connection Issues
-If MCP commands fail:
-1. Use `get_simulation_status` to test connectivity (fastest)
-2. Check CtrEditor is running with `get_ctreditor_status`
-3. Verify MCP server is active on port 5006
-
-### Simulation Issues (Rapid Response)
-If simulation behaves unexpectedly:
-```json
-// Quick diagnostic sequence
-{"tool": "get_simulation_status", "parameters": {}}
-{"tool": "search_debug_log", "parameters": {
- "pattern": "simulation|physics|error",
- "max_lines": 5
-}}
-```
-
-### Data Inconsistencies
-Efficient data validation:
-1. Use `get_simulation_status` for quick object count check
-2. Only call `list_objects` if counts don't match expectations
-3. Use targeted screenshots for visual verification
-
-## 💡 Enhanced Best Practices (UPDATED)
-
-### 🚀 Token Efficiency (Critical for Performance)
-
-#### **Ultra-Lightweight Commands (Use Liberally)**
-- `get_ctreditor_status` → 15-25 tokens response
-- `get_simulation_status` → 20-30 tokens response
-- `start/stop_simulation` → 10-15 tokens response
-
-#### **Medium Weight Commands (Use When Needed)**
-- `search_debug_log` → 50-200 tokens (with max_lines=5-10)
-- `build_project` → 100-500 tokens (errors only, filtered)
-- `create/update/delete_objects` → 30-100 tokens each
-
-#### **Heavy Commands (Use Sparingly)**
-- `list_objects` → 500-2000+ tokens (depends on object count)
-- `take_screenshot` → 100-300 tokens metadata + file
-
-### ⚡ Speed Optimization Patterns
-
-#### **Lightning-Fast Status Check (2-3 seconds)**
-```json
-{"tool": "get_simulation_status", "parameters": {}}
-```
-Returns: running state, object count, visibility in minimal tokens
-
-#### **Smart Error Detection (5-10 seconds)**
-```json
-{"tool": "search_debug_log", "parameters": {
- "pattern": "error|exception|fail",
- "max_lines": 3
-}}
-```
-Targeted error search instead of full console dump
-
-#### **Efficient Object Management**
-```json
-// Instead of list_objects -> update -> list_objects again:
-// 1. Single targeted update
-{"tool": "update_object", "parameters": {"id": "123", "properties": {...}}}
-
-// 2. Quick verification with status
-{"tool": "get_simulation_status", "parameters": {}}
-```
-
-### 🎯 Workflow Optimization
-
-#### **Development Iteration (Under 30 seconds)**
-```
-1. get_ctreditor_status (instant)
-2. build_project (filtered errors)
-3. start_simulation (instant)
-4. search_debug_log (targeted, max_lines=5)
-```
-
-#### **Bug Investigation (Under 15 seconds)**
-```
-1. search_debug_log (specific error pattern, max_lines=3)
-2. get_simulation_status (confirm state)
-3. targeted screenshot (only if visual needed)
-```
-
-#### **Performance Testing (Under 20 seconds)**
-```
-1. clear_debug_buffer
-2. start_simulation
-3. search_debug_log(pattern="fps|slow", max_lines=5)
-4. stop_simulation
-```
-
-### 🔍 Smart Debug Search Patterns
-
-#### **High-Value, Low-Token Patterns**
-- **Critical Errors**: `"error|exception|fail|crash"` (max_lines=3)
-- **Performance Issues**: `"fps|slow|memory|lag"` (max_lines=5)
-- **Simulation Problems**: `"simulation.*error|physics.*fail"` (max_lines=3)
-- **Object Issues**: `"object.*error|create.*fail|update.*error"` (max_lines=3)
-
-#### **Efficient Search Strategy**
-```json
-// Good: Specific, targeted
-{"pattern": "pump.*error", "max_lines": 3}
-
-// Avoid: Too broad, token-heavy
-{"pattern": ".*", "max_lines": 100}
-```
-
-### 📊 Data Minimization Techniques
-
-#### **Before Calling list_objects (Heavy)**
-Ask yourself:
-1. Do I need ALL objects or just count? → Use `get_simulation_status`
-2. Do I need specific object? → Search by type/name client-side
-3. Do I need visual confirmation? → Use targeted `take_screenshot`
-
-#### **Before Taking Screenshots**
-Consider:
-1. Is object data sufficient? → Use `list_objects` for position/properties
-2. Do I need full canvas? → Use area parameters for specific region
-3. Is this for documentation? → Full canvas OK
-4. Is this for debugging? → Targeted area preferred
-
-### 🔧 Advanced Efficiency Tips
-
-#### **Batch Operations (When Possible)**
-```json
-// Good: Single call for multiple deletes
-{"tool": "delete_objects", "parameters": {"ids": ["1", "2", "3"]}}
-
-// Avoid: Multiple single calls
-// delete_objects(id="1"), delete_objects(id="2"), delete_objects(id="3")
-```
-
-#### **State Caching Strategy**
-- Cache `get_simulation_status` results for 5-10 seconds
-- Only call `list_objects` when object count changes
-- Re-use screenshot data for multiple analyses
-
-#### **Conditional Command Execution**
-```json
-// Check state before expensive operations
-if (simulation_status.is_running) {
- // Only then do expensive analysis
- list_objects();
-}
-```
-
-### 🚨 Anti-Patterns (Avoid These)
-
-#### **Token Wasters**
-- ❌ Calling `list_objects` repeatedly without state change
-- ❌ Using `search_debug_log` without specific patterns
-- ❌ Taking full screenshots for simple status checks
-- ❌ Building project when no code changes made
-
-#### **Time Wasters**
-- ❌ Not checking `get_ctreditor_status` before operations
-- ❌ Using `stop_ctreditor` → `start_ctreditor` for simple resets
-- ❌ Clearing debug buffer unnecessarily
-- ❌ Taking screenshots without area parameters for small checks
-
-### 🎯 Context-Aware Command Selection
-
-#### **Development Context**
-Use: `build_project`, `search_debug_log`, `get_ctreditor_status`
-
-#### **Simulation Testing Context**
-Use: `start/stop_simulation`, `get_simulation_status`, targeted `search_debug_log`
-
-#### **Object Analysis Context**
-Use: `list_objects` (when needed), `update_object`, `get_simulation_status`
-
-#### **Visual Documentation Context**
-Use: `take_screenshot`, `list_objects` (for positions), `save_project`
-
-### 🔄 Recovery Patterns (Fast Error Recovery)
-
-#### **Process Recovery (5 seconds)**
-```json
-{"tool": "get_ctreditor_status", "parameters": {}}
-// If not running:
-{"tool": "start_ctreditor", "parameters": {}}
-```
-
-#### **Simulation Recovery (3 seconds)**
-```json
-{"tool": "stop_simulation", "parameters": {}}
-{"tool": "start_simulation", "parameters": {}}
-```
-
-#### **Debug Recovery (2 seconds)**
-```json
-{"tool": "stop_debug_listener", "parameters": {}}
-{"tool": "start_debug_listener", "parameters": {}}
-```
-
-### Safety
-- Always verify critical changes with `get_simulation_status`
-- Use appropriate units for all parameters (meters, Pascal, m³/s)
-- Stop simulation before major structural changes
-- Save project after significant modifications
-
-## 📋 Real-World Testing Results (VERIFIED PATTERNS)
-
-### ✅ **Proven Efficient Patterns (From Live Testing)**
-
-#### **Complete System Test (19/19 functions verified)**
-```json
-// 1. Process verification (instant)
-{"tool": "get_ctreditor_status", "parameters": {}}
-
-// 2. System assessment (25 tokens)
-{"tool": "get_simulation_status", "parameters": {}}
-
-// 3. Object discovery (when needed)
-{"tool": "list_objects", "parameters": {}}
-
-// 4. Visual confirmation (targeted)
-{"tool": "take_screenshot", "parameters": {}}
-```
-
-#### **Hydraulic System Analysis (Real Example)**
-**Found Objects** (from actual test):
-- 2 × Hydraulic Tanks (osHydTank): `TankPressure: 101000.0 Pascal`
-- 1 × Hydraulic Pump (osHydPump): `PumpHead: 75.0m, MaxFlow: 0.015 m³/s`
-- 2 × Hydraulic Pipes (osHydPipe): `Diameter: 0.05m, Length: 1.0m`
-
-**Key Properties Verified**:
-- Flow rates: `CurrentFlow: 0.0` (static state)
-- Pressure drops: `PressureDrop: 0.0` (no flow)
-- Connections: `Id_ComponenteA/B` linking system components
-
-#### **Build Error Handling (Real Example)**
-**Actual Error Encountered**: MSB3027/MSB3021 - File locked by CtrEditor process
-**Smart Response Pattern**:
-```json
-// 1. Check if process is blocking
-{"tool": "get_ctreditor_status", "parameters": {}}
-
-// 2. If running, stop for clean build
-{"tool": "stop_ctreditor", "parameters": {}}
-
-// 3. Clean and rebuild
-{"tool": "clean_project", "parameters": {}}
-{"tool": "build_project", "parameters": {}}
-```
-
-#### **Object Manipulation Workflow (Verified)**
-```json
-// 1. Create object (tested: osTextPlate, ID: 307325)
-{"tool": "create_object", "parameters": {
- "type": "osTextPlate", "x": 46, "y": 21
-}}
-
-// 2. Update properties (verified)
-{"tool": "update_object", "parameters": {
- "id": "307325",
- "properties": {"Text": "Sistema Hidráulico", "TextColor": "#FF0000FF"}
-}}
-
-// 3. Verify with status check (efficient)
-{"tool": "get_simulation_status", "parameters": {}}
-
-// 4. Clean up when done
-{"tool": "delete_objects", "parameters": {"ids": ["307325"]}}
-```
-
-### 📊 **Performance Metrics (Actual Results)**
-- **Screenshot**: 3.4MB file, 78×54 meter canvas captured
-- **Object types**: 33 types across 9 categories available
-- **Process startup**: ~5-10 seconds from stop to ready
-- **Simulation control**: Instant start/stop responses
-- **Debug search**: <2 seconds for targeted patterns
-
-### 🎯 **Object Type Categories (Complete List)**
-1. **Componentes Hidráulicos** (3): Pumps, Tanks, Pipes
-2. **Decorativos** (3): Images, Frames, Text Plates
-3. **Dinamicos** (2): Bottles, Bottle with Neck
-4. **Emuladores** (3): Bottle Generator, Filler, Tank
-5. **Estaticos** (9): Conveyors, Guides, Motors, Curves
-6. **Extraccion Datos** (2): Template Search, Tag Extraction
-7. **SensoresComandos** (6): Buttons, Encoders, Photocells, Sensors
-8. **TagsSignals** (3): Consensus, Analog Tags, Digital Tags
-9. **Traces** (2): Signal Tracers
-
-### 🔍 **Debug Patterns That Work (Tested)**
-```json
-// Critical errors (high value)
-{"pattern": "error|exception|fail", "max_lines": 3}
-
-// Performance issues
-{"pattern": "fps|slow|memory", "max_lines": 5}
-
-// Simulation specific
-{"pattern": "simulation|physics", "max_lines": 5}
-
-// Object operations
-{"pattern": "object.*create|update.*error", "max_lines": 3}
-```
-
-### ⚡ **Fastest Diagnostic Sequence (Under 10 seconds)**
-```json
-// 1. Health check (instant)
-{"tool": "get_simulation_status", "parameters": {}}
-
-// 2. Error scan (2-3 seconds)
-{"tool": "search_debug_log", "parameters": {
- "pattern": "error|fail", "max_lines": 3
-}}
-
-// 3. Process verification if needed (instant)
-{"tool": "get_ctreditor_status", "parameters": {}}
-```
-
-This sequence gives you complete system health in minimal time and tokens.
diff --git a/FREEZE_FIXES.md b/FREEZE_FIXES.md
deleted file mode 100644
index 1e33a17..0000000
--- a/FREEZE_FIXES.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# Soluciones Implementadas para Freezes en CtrEditor
-
-## Problema Identificado
-La aplicación CtrEditor experimentaba freezes relacionados con I/O Completion Ports, específicamente en:
-```
-System.Private.CoreLib.dll!System.Threading.PortableThreadPool.IOCompletionPoller.Poll()
-Interop.Kernel32.GetQueuedCompletionStatusEx(..., Timeout.Infinite, ...)
-```
-
-## Causas Raíz y Soluciones Implementadas
-
-### 1. **MCPServer - AcceptTcpClientAsync Sin Cancelación**
-**Archivo:** `Services/MCPServer.cs`
-**Problema:** El método `AcceptTcpClientAsync()` sin `CancellationToken` causaba bloqueos indefinidos en el I/O completion port.
-
-**Solución Implementada:**
-```csharp
-// ANTES (PROBLEMÁTICO)
-var tcpClient = await _tcpListener.AcceptTcpClientAsync();
-
-// DESPUÉS (CORREGIDO)
-var acceptTask = _tcpListener.AcceptTcpClientAsync();
-var delayTask = Task.Delay(1000, cancellationToken);
-var completedTask = await Task.WhenAny(acceptTask, delayTask);
-
-if (completedTask == acceptTask && !cancellationToken.IsCancellationRequested)
-{
- var tcpClient = await acceptTask;
- // Procesar cliente...
-}
-```
-
-### 2. **StateManager - Bloqueo en Dispose**
-**Archivo:** `DataStates/StateManager.cs`
-**Problema:** Uso de `.Wait()` en método `Dispose()` puede causar deadlocks.
-
-**Solución Implementada:**
-```csharp
-// ANTES (PROBLEMÁTICO)
-SaveAllAsync().Wait();
-
-// DESPUÉS (CORREGIDO)
-Task.Run(async () => await SaveAllAsync()).Wait(TimeSpan.FromSeconds(10));
-```
-
-### 3. **StateManager - Antipatrón Task.Run + Dispatcher.Invoke**
-**Archivo:** `DataStates/StateManager.cs`
-**Problema:** Patrón ineficiente que puede causar contención de hilos.
-
-**Solución Implementada:**
-```csharp
-// ANTES (PROBLEMÁTICO)
-await Task.Run(() =>
-{
- Application.Current.Dispatcher.Invoke(() =>
- {
- CrearUserControlDesdeObjetoSimulable(obj);
- });
-});
-
-// DESPUÉS (CORREGIDO)
-await Application.Current.Dispatcher.InvokeAsync(() =>
-{
- CrearUserControlDesdeObjetoSimulable(obj);
-});
-```
-
-### 4. **HttpClient - Falta de ConfigureAwait(false)**
-**Archivo:** `IA/gtpask.cs`
-**Problema:** Operaciones HTTP sin `ConfigureAwait(false)` pueden causar deadlocks en UI thread.
-
-**Solución Implementada:**
-```csharp
-// ANTES (PROBLEMÁTICO)
-using var response = await _httpClient.PostAsync(endpoint, content);
-var responseContent = await response.Content.ReadAsStringAsync();
-
-// DESPUÉS (CORREGIDO)
-using var response = await _httpClient.PostAsync(endpoint, content).ConfigureAwait(false);
-var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-```
-
-## Resultado
-- Eliminación de bloqueos indefinitos en I/O Completion Ports
-- Prevención de deadlocks en operaciones de red y UI
-- Mejora en la capacidad de cancelación de operaciones TCP
-- Manejo más robusto de operaciones asíncronas
-
-## Verificación
-- ✅ Proyecto compila sin errores
-- ✅ DebugConsoleServer ya tenía la misma corrección implementada
-- ✅ Aplicados timeouts y manejo de excepciones apropiados
-
-## Próximos Pasos Recomendados
-1. Probar la aplicación en escenarios que anteriormente causaban freezes
-2. Monitorear logs para verificar que las operaciones TCP se cancelan correctamente
-3. Considerar aplicar `ConfigureAwait(false)` a otras operaciones asíncronas en el proyecto
-4. Revisar otros usos de operaciones síncronas en contextos asíncronos
-
-## Archivos Modificados
-- `Services/MCPServer.cs` - Corrección de AcceptTcpClientAsync
-- `DataStates/StateManager.cs` - Corrección de Dispose y antipatrón Task.Run
-- `IA/gtpask.cs` - Agregado de ConfigureAwait(false) para operaciones HTTP
diff --git a/PYTHON_EXECUTION_README.md b/PYTHON_EXECUTION_README.md
deleted file mode 100644
index d3fbe45..0000000
--- a/PYTHON_EXECUTION_README.md
+++ /dev/null
@@ -1,146 +0,0 @@
-# Python Execution in CtrEditor via MCP
-
-Esta funcionalidad permite ejecutar scripts Python directamente dentro de CtrEditor para debug y testing, con acceso completo a los objetos de la aplicación.
-
-## Características
-
-- **Ejecución de Python sin instalaciones externas**: Usa IronPython 3.4.2 integrado
-- **Acceso completo a objetos**: MainViewModel, Canvas, ObjetosSimulables
-- **Timeout configurable**: Previene scripts que se cuelguen
-- **Variables de retorno**: Obtén valores específicos del script
-- **Help integrado**: Documentación automática de objetos disponibles
-
-## Objetos Disponibles en Python
-
-### Variables Globales
-- `app`: MainViewModel - El modelo de vista principal de la aplicación
-- `canvas`: Canvas - El canvas principal donde se muestran los objetos
-- `objects`: ObservableCollection - Colección de todos los objetos simulables
-- `get_objects()`: Función que retorna una lista de todos los objetos simulables
-
-### Librerías Disponibles
-- `sys`, `math`, `time`, `json`, `random`
-
-## Herramientas MCP Disponibles
-
-### execute_python
-Ejecuta código Python con acceso a objetos de CtrEditor.
-
-**Parámetros:**
-- `code` (requerido): El código Python a ejecutar
-- `return_variables` (opcional): Lista de nombres de variables a retornar
-- `timeout_seconds` (opcional): Timeout en segundos (default: 30)
-
-**Ejemplo:**
-```json
-{
- "name": "execute_python",
- "arguments": {
- "code": "count = len(objects)\nprint(f'Total objects: {count}')",
- "return_variables": ["count"],
- "timeout_seconds": 10
- }
-}
-```
-
-### python_help
-Obtiene ayuda sobre objetos y métodos disponibles.
-
-**Parámetros:**
-- `object_name` (opcional): Nombre específico del objeto para obtener ayuda
-
-**Ejemplos:**
-```json
-{
- "name": "python_help",
- "arguments": {}
-}
-```
-
-```json
-{
- "name": "python_help",
- "arguments": {
- "object_name": "app"
- }
-}
-```
-
-## Ejemplos de Uso
-
-### 1. Contar objetos
-```python
-# Contar todos los objetos
-total = len(objects)
-print(f"Total objects: {total}")
-
-# Filtrar objetos por tipo
-conveyors = [obj for obj in objects if 'Conveyor' in str(type(obj))]
-print(f"Conveyors: {len(conveyors)}")
-```
-
-### 2. Inspeccionar canvas
-```python
-# Obtener información del canvas
-width = canvas.Width
-height = canvas.Height
-print(f"Canvas size: {width}x{height}")
-
-# Verificar estado de simulación
-is_running = app.IsSimulationRunning
-print(f"Simulation running: {is_running}")
-```
-
-### 3. Modificar objetos (con precaución)
-```python
-# Buscar un objeto específico
-for obj in objects:
- if hasattr(obj, 'Nombre') and obj.Nombre == "MiObjeto":
- print(f"Found object: {obj.Nombre}")
- # Modificar propiedades si es necesario
- if hasattr(obj, 'Visible'):
- obj.Visible = not obj.Visible
- break
-```
-
-### 4. Debugging avanzado
-```python
-# Obtener propiedades de todos los objetos
-for i, obj in enumerate(objects):
- obj_type = type(obj).__name__
- properties = [prop for prop in dir(obj) if not prop.startswith('_')]
- print(f"Object {i}: {obj_type} - {len(properties)} properties")
-```
-
-## Seguridad y Precauciones
-
-⚠️ **IMPORTANTE**: Esta funcionalidad está diseñada solo para debug y testing. NO está pensada para uso en producción.
-
-- El código Python se ejecuta en el mismo proceso que CtrEditor
-- Tiene acceso completo a todos los objetos de la aplicación
-- No hay restricciones de seguridad implementadas
-- Use solo en entornos de desarrollo controlados
-
-## Configuración
-
-Las herramientas se configuran automáticamente cuando se inicia el MCPServer. No se requiere configuración adicional.
-
-## Solución de Problemas
-
-### Error: "Python environment initialization failed"
-- Verifica que IronPython esté correctamente instalado via NuGet
-- Revisa los logs de debug para más detalles
-
-### Error: "Python execution timed out"
-- Reduce la complejidad del script
-- Aumenta el timeout_seconds si es necesario
-- Verifica que no haya bucles infinitos
-
-### Variables no encontradas
-- Usa `python_help` para ver qué objetos están disponibles
-- Verifica que los objetos existan antes de usarlos
-- Usa `hasattr()` para verificar propiedades antes de accederlas
-
-## Logging
-
-Todos los eventos de ejecución Python se registran en el log de debug de CtrEditor con el prefijo `[MCP Server]`.
diff --git a/Services/MCPServer.cs b/Services/MCPServer.cs
index a50dc72..69b6328 100644
--- a/Services/MCPServer.cs
+++ b/Services/MCPServer.cs
@@ -36,6 +36,7 @@ namespace CtrEditor.Services
private CancellationTokenSource _cancellationTokenSource;
private bool _isRunning;
private readonly object _lockObject = new object();
+ private ScreenshotManager _screenshotManager;
// Simulation timing tracking
private readonly Stopwatch _simulationStopwatch;
@@ -58,10 +59,30 @@ namespace CtrEditor.Services
_totalSimulationMilliseconds = 0;
_lastSimulationStatus = false;
+ // ScreenshotManager se inicializará de forma lazy cuando se necesite
+ _screenshotManager = null;
+
// Initialize Python environment
InitializePythonEnvironment();
}
+ ///
+ /// Obtiene el ScreenshotManager, inicializándolo si es necesario
+ ///
+ private ScreenshotManager GetScreenshotManager()
+ {
+ if (_screenshotManager == null)
+ {
+ var canvas = _mainViewModel?.MainCanvas;
+ if (canvas == null)
+ {
+ throw new InvalidOperationException("Canvas no está disponible. Asegúrate de que la UI esté completamente cargada.");
+ }
+ _screenshotManager = new ScreenshotManager(_mainViewModel, canvas);
+ }
+ return _screenshotManager;
+ }
+
///
/// Inicia el servidor MCP TCP
///
@@ -209,6 +230,7 @@ namespace CtrEditor.Services
new { name = "get_simulation_status", description = "Get current simulation status" },
new { name = "get_plc_status", description = "Get PLC connection status" },
new { name = "take_screenshot", description = "Take a screenshot of the canvas" },
+ new { name = "take_object_screenshot", description = "Take a screenshot of specific objects by their IDs" },
new { name = "save_project", description = "Save the current project" },
new { name = "reset_simulation_timing", description = "Reset simulation timing counters" }
}
@@ -340,10 +362,41 @@ namespace CtrEditor.Services
height = new {
type = "number",
description = "Height in meters for partial capture (optional)"
+ },
+ center_coordinates = new {
+ type = "boolean",
+ description = "If true, x and y parameters represent the center of the capture area instead of top-left corner. Defaults to false for backward compatibility."
}
}
}
},
+ new {
+ name = "take_object_screenshot",
+ description = "Take a screenshot of specific objects by their IDs. This automatically calculates the bounding box of the specified objects and captures only that area with optional padding.",
+ inputSchema = new {
+ type = "object",
+ properties = new {
+ object_ids = new {
+ type = "array",
+ items = new { type = "string" },
+ description = "Array of object IDs to capture. Can be a single object or multiple objects."
+ },
+ padding_meters = new {
+ type = "number",
+ description = "Additional padding around objects in meters. Defaults to 0.5 meters."
+ },
+ filename = new {
+ type = "string",
+ description = "Optional filename for the screenshot. Defaults to timestamp-based name."
+ },
+ include_background = new {
+ type = "boolean",
+ description = "Whether to include canvas background image. Defaults to false."
+ }
+ },
+ required = new[] { "object_ids" }
+ }
+ },
new {
name = "start_simulation",
description = "Start the physics simulation",
@@ -476,6 +529,7 @@ namespace CtrEditor.Services
"get_simulation_status" => GetSimulationStatus(),
"get_plc_status" => GetPlcStatus(),
"take_screenshot" => TakeScreenshot(arguments),
+ "take_object_screenshot" => TakeObjectScreenshot(arguments),
"save_project" => SaveProject(),
"reset_simulation_timing" => ResetSimulationTiming(),
"execute_python" => ExecutePython(arguments),
@@ -966,72 +1020,85 @@ namespace CtrEditor.Services
}
///
- /// Toma una captura de pantalla del canvas
+ /// Toma una captura de pantalla del canvas usando el nuevo ScreenshotManager
///
private object TakeScreenshot(JObject arguments)
{
try
{
- // Parámetros de screenshot
- var filename = arguments["filename"]?.ToString() ?? $"screenshot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
- var includeBackground = arguments["include_background"]?.ToObject() ?? false; // Por defecto false
+ var filename = arguments["filename"]?.ToString();
+ var includeBackground = arguments["include_background"]?.ToObject() ?? false;
+ var saveFile = arguments["save_file"]?.ToObject() ?? true;
+ var returnBase64 = arguments["return_base64"]?.ToObject() ?? true;
+
var x = arguments["x"]?.ToObject();
var y = arguments["y"]?.ToObject();
var width = arguments["width"]?.ToObject();
var height = arguments["height"]?.ToObject();
+ var centerCoordinates = arguments["center_coordinates"]?.ToObject() ?? false;
- // Asegurar extensión .png
- if (!filename.ToLower().EndsWith(".png"))
- filename += ".png";
+ // Obtener ScreenshotManager de forma lazy
+ var screenshotManager = GetScreenshotManager();
+ ScreenshotResult result;
- // Crear subdirectorio screenshots
- var screenshotsDir = System.IO.Path.Combine(EstadoPersistente.Instance.directorio, "screenshots");
- Directory.CreateDirectory(screenshotsDir);
-
- // Obtener ruta completa - siempre en subdirectorio screenshots a menos que sea ruta absoluta
- var fullPath = System.IO.Path.IsPathRooted(filename) ? filename : System.IO.Path.Combine(screenshotsDir, filename);
-
- // Obtener información del canvas para detalles
- var canvas = _mainViewModel.MainCanvas;
- var canvasWidth = canvas?.ActualWidth ?? 0;
- var canvasHeight = canvas?.ActualHeight ?? 0;
- var canvasWidthMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)canvasWidth);
- var canvasHeightMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)canvasHeight);
-
- // Tomar screenshot
- var success = TakeCanvasScreenshot(fullPath, includeBackground, x, y, width, height);
-
- if (success)
+ if (x.HasValue && y.HasValue && width.HasValue && height.HasValue)
+ {
+ // Captura de área específica
+ if (centerCoordinates)
+ {
+ result = screenshotManager.CaptureCenteredArea(
+ x.Value, y.Value, width.Value, height.Value,
+ filename, includeBackground, saveFile, returnBase64);
+ }
+ else
+ {
+ result = screenshotManager.CaptureArea(
+ x.Value, y.Value, width.Value, height.Value,
+ filename, includeBackground, saveFile, returnBase64);
+ }
+ }
+ else
+ {
+ // Captura de todo el canvas
+ result = screenshotManager.CaptureFullCanvas(
+ filename, includeBackground, saveFile, returnBase64);
+ }
+
+ if (result.Success)
{
- var fileInfo = new FileInfo(fullPath);
return new
{
success = true,
- filename = System.IO.Path.GetFileName(fullPath),
- full_path = fullPath,
- directory = System.IO.Path.GetDirectoryName(fullPath),
- file_size_bytes = fileInfo.Length,
- timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+ filename = result.FileName,
+ full_path = result.FilePath,
+ directory = result.Directory,
+ file_size_bytes = result.FileSizeBytes,
+ timestamp = result.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
message = "Screenshot saved successfully",
+ base64_data = returnBase64 ? result.Base64Data : null,
canvas_info = new
{
- canvas_width_pixels = canvasWidth,
- canvas_height_pixels = canvasHeight,
- canvas_width_meters = Math.Round(canvasWidthMeters, 3),
- canvas_height_meters = Math.Round(canvasHeightMeters, 3)
+ canvas_width_pixels = _mainViewModel.MainCanvas?.ActualWidth ?? 0,
+ canvas_height_pixels = _mainViewModel.MainCanvas?.ActualHeight ?? 0,
+ canvas_width_meters = result.CaptureArea?.Width ?? 0,
+ canvas_height_meters = result.CaptureArea?.Height ?? 0
},
capture_info = new
{
- include_background = includeBackground,
+ include_background = result.IncludeBackground,
area_specified = x.HasValue && y.HasValue && width.HasValue && height.HasValue,
- area = x.HasValue ? new {
- x = Math.Round(x.Value, 3),
- y = Math.Round(y.Value, 3),
- width = Math.Round(width.Value, 3),
- height = Math.Round(height.Value, 3),
+ area = result.CaptureArea != null ? new
+ {
+ x = Math.Round(result.CaptureArea.Left, 3),
+ y = Math.Round(result.CaptureArea.Top, 3),
+ width = Math.Round(result.CaptureArea.Width, 3),
+ height = Math.Round(result.CaptureArea.Height, 3),
+ center_x = Math.Round(result.CaptureArea.CenterX, 3),
+ center_y = Math.Round(result.CaptureArea.CenterY, 3),
units = "meters"
} : null,
- capture_type = x.HasValue ? "partial" : "full_canvas"
+ capture_type = result.CaptureType.ToString().ToLower(),
+ center_coordinates = centerCoordinates
}
};
}
@@ -1040,15 +1107,16 @@ namespace CtrEditor.Services
return new
{
success = false,
- error = "Failed to capture screenshot",
- attempted_path = fullPath
+ error = result.ErrorMessage,
+ error_type = result.ErrorType
};
}
}
catch (Exception ex)
{
- return new {
- success = false,
+ return new
+ {
+ success = false,
error = ex.Message,
error_type = ex.GetType().Name
};
@@ -1076,156 +1144,96 @@ namespace CtrEditor.Services
}
}
- #endregion
-
- #region Screenshot Implementation
-
///
- /// Toma una captura de pantalla del canvas
+ /// Toma una captura de pantalla de objetos específicos por sus IDs usando el nuevo ScreenshotManager
///
- private bool TakeCanvasScreenshot(string filePath, bool includeBackground = false,
- float? x = null, float? y = null, float? width = null, float? height = null)
+ private object TakeObjectScreenshot(JObject arguments)
{
try
{
- var canvas = _mainViewModel.MainCanvas;
- if (canvas == null)
+ var objectIds = arguments["object_ids"]?.ToObject();
+ if (objectIds == null || objectIds.Length == 0)
+ throw new ArgumentException("object_ids is required and must not be empty");
+
+ var paddingMeters = arguments["padding_meters"]?.ToObject() ?? 0.5f;
+ var filename = arguments["filename"]?.ToString();
+ var includeBackground = arguments["include_background"]?.ToObject() ?? false;
+ var saveFile = arguments["save_file"]?.ToObject() ?? true;
+ var returnBase64 = arguments["return_base64"]?.ToObject() ?? true;
+
+ // Obtener ScreenshotManager de forma lazy y usar el nuevo método
+ var screenshotManager = GetScreenshotManager();
+ var result = screenshotManager.CaptureObjects(
+ objectIds, paddingMeters, filename, includeBackground, saveFile, returnBase64);
+
+ if (result.Success)
{
- Debug.WriteLine("[MCP Server] Canvas is null");
- return false;
- }
-
- // Asegurar que el canvas esté renderizado
- canvas.UpdateLayout();
-
- // Determinar el área a capturar
- Rect captureRect;
- if (x.HasValue && y.HasValue && width.HasValue && height.HasValue)
- {
- // Convertir coordenadas de metros a píxeles
- var pixelX = PixelToMeter.Instance.calc.MetersToPixels(x.Value);
- var pixelY = PixelToMeter.Instance.calc.MetersToPixels(y.Value);
- var pixelWidth = PixelToMeter.Instance.calc.MetersToPixels(width.Value);
- var pixelHeight = PixelToMeter.Instance.calc.MetersToPixels(height.Value);
-
- captureRect = new Rect(pixelX, pixelY, pixelWidth, pixelHeight);
+ return new
+ {
+ success = true,
+ filename = result.FileName,
+ full_path = result.FilePath,
+ directory = result.Directory,
+ file_size_bytes = result.FileSizeBytes,
+ timestamp = result.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
+ message = "Object screenshot saved successfully",
+ base64_data = returnBase64 ? result.Base64Data : null,
+ captured_objects = result.CapturedObjects.Select(obj => new
+ {
+ id = obj.Id,
+ name = obj.Name,
+ type = obj.Type,
+ position = new { x = obj.Left, y = obj.Top },
+ center = new { x = obj.CenterX, y = obj.CenterY },
+ dimensions = new { width = obj.Width, height = obj.Height }
+ }).ToArray(),
+ bounding_box = new
+ {
+ left = Math.Round(result.BoundingBox.Left, 3),
+ top = Math.Round(result.BoundingBox.Top, 3),
+ right = Math.Round(result.BoundingBox.Left + result.BoundingBox.Width, 3),
+ bottom = Math.Round(result.BoundingBox.Top + result.BoundingBox.Height, 3),
+ center_x = Math.Round(result.BoundingBox.CenterX, 3),
+ center_y = Math.Round(result.BoundingBox.CenterY, 3),
+ width = Math.Round(result.BoundingBox.Width, 3),
+ height = Math.Round(result.BoundingBox.Height, 3)
+ },
+ capture_area = new
+ {
+ left = Math.Round(result.CaptureArea.Left, 3),
+ top = Math.Round(result.CaptureArea.Top, 3),
+ center_x = Math.Round(result.CaptureArea.CenterX, 3),
+ center_y = Math.Round(result.CaptureArea.CenterY, 3),
+ width = Math.Round(result.CaptureArea.Width, 3),
+ height = Math.Round(result.CaptureArea.Height, 3),
+ padding_meters = result.PaddingMeters
+ },
+ capture_info = new
+ {
+ include_background = result.IncludeBackground,
+ capture_type = result.CaptureType.ToString().ToLower(),
+ objects_count = result.CapturedObjects.Count
+ }
+ };
}
else
{
- // Capturar todo el canvas
- captureRect = new Rect(0, 0, canvas.ActualWidth, canvas.ActualHeight);
- }
-
- // Validar dimensiones
- if (captureRect.Width <= 0 || captureRect.Height <= 0)
- {
- Debug.WriteLine($"[MCP Server] Invalid capture dimensions: {captureRect}");
- return false;
- }
-
- Debug.WriteLine($"[MCP Server] Capturing area: {captureRect}, Canvas size: {canvas.ActualWidth}x{canvas.ActualHeight}");
-
- // Crear RenderTargetBitmap con alta resolución
- // Usar factor de escala para mejorar calidad en capturas parciales
- var scaleFactor = (x.HasValue && y.HasValue) ? 3.0 : 2.0; // Mayor escala para áreas parciales
- var renderWidth = Math.Max(1, (int)(captureRect.Width * scaleFactor));
- var renderHeight = Math.Max(1, (int)(captureRect.Height * scaleFactor));
- var dpi = 96 * scaleFactor; // Aumentar DPI proporcionalmente
-
- var renderBitmap = new RenderTargetBitmap(
- renderWidth,
- renderHeight,
- dpi, // DPI X
- dpi, // DPI Y
- PixelFormats.Pbgra32);
-
- // Crear un Canvas temporal para renderizado con escala mejorada
- var tempCanvas = new Canvas()
- {
- Width = captureRect.Width * scaleFactor,
- Height = captureRect.Height * scaleFactor,
- Background = includeBackground ? canvas.Background : Brushes.White
- };
-
- // Aplicar escala al canvas
- tempCanvas.RenderTransform = new ScaleTransform(scaleFactor, scaleFactor);
-
- // Clonar elementos visibles del canvas principal
- foreach (UIElement child in canvas.Children)
- {
- if (child.Visibility == Visibility.Visible)
+ return new
{
- try
- {
- // Obtener posición del elemento
- var left = Canvas.GetLeft(child);
- var top = Canvas.GetTop(child);
-
- // Verificar si está en el área de captura
- var elementRect = new Rect(
- double.IsNaN(left) ? 0 : left,
- double.IsNaN(top) ? 0 : top,
- child.RenderSize.Width,
- child.RenderSize.Height);
-
- if (captureRect.IntersectsWith(elementRect) || (!x.HasValue && !y.HasValue))
- {
- // Crear una representación visual del elemento
- var visualBrush = new VisualBrush(child)
- {
- Stretch = Stretch.None,
- AlignmentX = AlignmentX.Left,
- AlignmentY = AlignmentY.Top
- };
-
- var rect = new Rectangle()
- {
- Width = child.RenderSize.Width * scaleFactor,
- Height = child.RenderSize.Height * scaleFactor,
- Fill = visualBrush
- };
-
- // Posicionar relativo al área de captura con escala
- Canvas.SetLeft(rect, (elementRect.X - captureRect.X) * scaleFactor);
- Canvas.SetTop(rect, (elementRect.Y - captureRect.Y) * scaleFactor);
-
- tempCanvas.Children.Add(rect);
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine($"[MCP Server] Error processing child element: {ex.Message}");
- }
- }
+ success = false,
+ error = result.ErrorMessage,
+ error_type = result.ErrorType
+ };
}
-
- // Forzar layout del canvas temporal con las nuevas dimensiones escaladas
- var scaledSize = new Size(captureRect.Width * scaleFactor, captureRect.Height * scaleFactor);
- tempCanvas.Measure(scaledSize);
- tempCanvas.Arrange(new Rect(0, 0, scaledSize.Width, scaledSize.Height));
- tempCanvas.UpdateLayout();
-
- // Renderizar
- renderBitmap.Render(tempCanvas);
-
- // Guardar imagen
- var encoder = new PngBitmapEncoder();
- encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
-
- Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath));
- using (var fileStream = new FileStream(filePath, FileMode.Create))
- {
- encoder.Save(fileStream);
- }
-
- Debug.WriteLine($"[MCP Server] Screenshot saved successfully: {filePath}");
- return true;
}
catch (Exception ex)
{
- Debug.WriteLine($"[MCP Server] Error taking screenshot: {ex.Message}");
- Debug.WriteLine($"[MCP Server] Stack trace: {ex.StackTrace}");
- return false;
+ return new
+ {
+ success = false,
+ error = ex.Message,
+ error_type = ex.GetType().Name
+ };
}
}
diff --git a/Services/ScreenshotManager.cs b/Services/ScreenshotManager.cs
new file mode 100644
index 0000000..60e0438
--- /dev/null
+++ b/Services/ScreenshotManager.cs
@@ -0,0 +1,625 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using CtrEditor.ObjetosSim;
+using System.Diagnostics;
+
+namespace CtrEditor.Services
+{
+ ///
+ /// Gestor centralizado de capturas de pantalla del canvas.
+ /// Proporciona funcionalidades para capturar:
+ /// 1. Objetos específicos por ID
+ /// 2. Áreas específicas en coordenadas de metros
+ /// 3. Todo el canvas
+ ///
+ /// Utiliza la misma lógica que ObjectManipulationManager para calcular
+ /// las dimensiones reales de los objetos en el canvas.
+ ///
+ public class ScreenshotManager
+ {
+ private readonly MainViewModel _mainViewModel;
+ private readonly Canvas _canvas;
+ private readonly string _defaultScreenshotsDirectory;
+
+ public ScreenshotManager(MainViewModel mainViewModel, Canvas canvas)
+ {
+ _mainViewModel = mainViewModel ?? throw new ArgumentNullException(nameof(mainViewModel));
+ _canvas = canvas ?? throw new ArgumentNullException(nameof(canvas));
+ _defaultScreenshotsDirectory = System.IO.Path.Combine(EstadoPersistente.Instance.directorio, "screenshots");
+ }
+
+ #region Public Methods
+
+ ///
+ /// Captura screenshot de objetos específicos por sus IDs
+ ///
+ /// Array de IDs de objetos a capturar
+ /// Padding adicional alrededor de los objetos en metros
+ /// Nombre del archivo (opcional)
+ /// Si incluir imagen de fondo
+ /// Si guardar el archivo
+ /// Si retornar la imagen como base64
+ /// Resultado de la captura
+ public ScreenshotResult CaptureObjects(
+ string[] objectIds,
+ float paddingMeters = 0.5f,
+ string filename = null,
+ bool includeBackground = false,
+ bool saveToFile = true,
+ bool returnBase64 = true)
+ {
+ try
+ {
+ if (objectIds == null || objectIds.Length == 0)
+ throw new ArgumentException("objectIds no puede estar vacío");
+
+ // Buscar objetos por ID
+ var targetObjects = FindObjectsByIds(objectIds);
+ if (!targetObjects.Any())
+ throw new ArgumentException($"No se encontraron objetos visibles con los IDs: {string.Join(", ", objectIds)}");
+
+ // Calcular bounding box usando la misma lógica que ObjectManipulationManager
+ var boundingBox = CalculateObjectsBoundingBox(targetObjects);
+
+ // Aplicar padding
+ var captureArea = new ScreenshotArea
+ {
+ Left = boundingBox.Left - paddingMeters,
+ Top = boundingBox.Top - paddingMeters,
+ Width = boundingBox.Width + (paddingMeters * 2),
+ Height = boundingBox.Height + (paddingMeters * 2)
+ };
+
+ // Asegurar dimensiones mínimas
+ captureArea.Width = Math.Max(captureArea.Width, 0.1f);
+ captureArea.Height = Math.Max(captureArea.Height, 0.1f);
+
+ // Capturar la imagen
+ var bitmap = CaptureCanvasArea(captureArea, includeBackground);
+
+ // Preparar resultado
+ var result = new ScreenshotResult
+ {
+ Success = true,
+ Bitmap = bitmap,
+ CaptureType = ScreenshotType.Objects,
+ CapturedObjects = targetObjects.Select(obj => new CapturedObjectInfo
+ {
+ Id = obj.Id.Value.ToString(),
+ Name = obj.Nombre,
+ Type = obj.GetType().Name,
+ Left = obj.Left,
+ Top = obj.Top,
+ Width = obj.Ancho,
+ Height = obj.Alto,
+ CenterX = obj.Left + obj.Ancho / 2,
+ CenterY = obj.Top + obj.Alto / 2
+ }).ToList(),
+ BoundingBox = new AreaInfo
+ {
+ Left = boundingBox.Left,
+ Top = boundingBox.Top,
+ Width = boundingBox.Width,
+ Height = boundingBox.Height,
+ CenterX = boundingBox.Left + boundingBox.Width / 2,
+ CenterY = boundingBox.Top + boundingBox.Height / 2
+ },
+ CaptureArea = new AreaInfo
+ {
+ Left = captureArea.Left,
+ Top = captureArea.Top,
+ Width = captureArea.Width,
+ Height = captureArea.Height,
+ CenterX = captureArea.Left + captureArea.Width / 2,
+ CenterY = captureArea.Top + captureArea.Height / 2
+ },
+ PaddingMeters = paddingMeters,
+ IncludeBackground = includeBackground
+ };
+
+ // Guardar archivo y/o generar base64
+ return ProcessScreenshotOutput(result, filename, saveToFile, returnBase64);
+ }
+ catch (Exception ex)
+ {
+ return new ScreenshotResult
+ {
+ Success = false,
+ ErrorMessage = ex.Message,
+ ErrorType = ex.GetType().Name
+ };
+ }
+ }
+
+ ///
+ /// Captura screenshot de un área específica en coordenadas de metros
+ ///
+ /// Coordenada X izquierda en metros
+ /// Coordenada Y superior en metros
+ /// Ancho en metros
+ /// Alto en metros
+ /// Nombre del archivo (opcional)
+ /// Si incluir imagen de fondo
+ /// Si guardar el archivo
+ /// Si retornar la imagen como base64
+ /// Resultado de la captura
+ public ScreenshotResult CaptureArea(
+ float left,
+ float top,
+ float width,
+ float height,
+ string filename = null,
+ bool includeBackground = false,
+ bool saveToFile = true,
+ bool returnBase64 = true)
+ {
+ try
+ {
+ if (width <= 0 || height <= 0)
+ throw new ArgumentException("Width y height deben ser mayores que 0");
+
+ var captureArea = new ScreenshotArea
+ {
+ Left = left,
+ Top = top,
+ Width = width,
+ Height = height
+ };
+
+ var bitmap = CaptureCanvasArea(captureArea, includeBackground);
+
+ var result = new ScreenshotResult
+ {
+ Success = true,
+ Bitmap = bitmap,
+ CaptureType = ScreenshotType.Area,
+ CaptureArea = new AreaInfo
+ {
+ Left = left,
+ Top = top,
+ Width = width,
+ Height = height,
+ CenterX = left + width / 2,
+ CenterY = top + height / 2
+ },
+ IncludeBackground = includeBackground
+ };
+
+ return ProcessScreenshotOutput(result, filename, saveToFile, returnBase64);
+ }
+ catch (Exception ex)
+ {
+ return new ScreenshotResult
+ {
+ Success = false,
+ ErrorMessage = ex.Message,
+ ErrorType = ex.GetType().Name
+ };
+ }
+ }
+
+ ///
+ /// Captura screenshot de un área centrada en coordenadas específicas
+ ///
+ /// Coordenada X del centro en metros
+ /// Coordenada Y del centro en metros
+ /// Ancho en metros
+ /// Alto en metros
+ /// Nombre del archivo (opcional)
+ /// Si incluir imagen de fondo
+ /// Si guardar el archivo
+ /// Si retornar la imagen como base64
+ /// Resultado de la captura
+ public ScreenshotResult CaptureCenteredArea(
+ float centerX,
+ float centerY,
+ float width,
+ float height,
+ string filename = null,
+ bool includeBackground = false,
+ bool saveToFile = true,
+ bool returnBase64 = true)
+ {
+ var left = centerX - width / 2;
+ var top = centerY - height / 2;
+
+ return CaptureArea(left, top, width, height, filename, includeBackground, saveToFile, returnBase64);
+ }
+
+ ///
+ /// Captura screenshot de todo el canvas
+ ///
+ /// Nombre del archivo (opcional)
+ /// Si incluir imagen de fondo
+ /// Si guardar el archivo
+ /// Si retornar la imagen como base64
+ /// Resultado de la captura
+ public ScreenshotResult CaptureFullCanvas(
+ string filename = null,
+ bool includeBackground = true,
+ bool saveToFile = true,
+ bool returnBase64 = true)
+ {
+ try
+ {
+ var bitmap = CaptureEntireCanvas(includeBackground);
+
+ var canvasWidthMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)_canvas.ActualWidth);
+ var canvasHeightMeters = PixelToMeter.Instance.calc.PixelsToMeters((float)_canvas.ActualHeight);
+
+ var result = new ScreenshotResult
+ {
+ Success = true,
+ Bitmap = bitmap,
+ CaptureType = ScreenshotType.FullCanvas,
+ CaptureArea = new AreaInfo
+ {
+ Left = 0,
+ Top = 0,
+ Width = canvasWidthMeters,
+ Height = canvasHeightMeters,
+ CenterX = canvasWidthMeters / 2,
+ CenterY = canvasHeightMeters / 2
+ },
+ IncludeBackground = includeBackground
+ };
+
+ return ProcessScreenshotOutput(result, filename, saveToFile, returnBase64);
+ }
+ catch (Exception ex)
+ {
+ return new ScreenshotResult
+ {
+ Success = false,
+ ErrorMessage = ex.Message,
+ ErrorType = ex.GetType().Name
+ };
+ }
+ }
+
+ #endregion
+
+ #region Private Helper Methods
+
+ ///
+ /// Busca objetos por sus IDs
+ ///
+ private List FindObjectsByIds(string[] objectIds)
+ {
+ var allObjects = _mainViewModel.ObjetosSimulables.ToList();
+ var result = new List();
+
+ foreach (var objectId in objectIds)
+ {
+ var obj = allObjects.FirstOrDefault(o => o.Id.Value.ToString() == objectId);
+ if (obj != null && obj.Show_On_This_Page)
+ {
+ result.Add(obj);
+ Console.WriteLine($"DEBUG: Object {objectId} ADDED - Type: {obj.GetType().Name}, Left: {obj.Left}, Top: {obj.Top}");
+ }
+ else
+ {
+ Console.WriteLine($"DEBUG: Object {objectId} REJECTED - Found: {obj != null}, Show_On_This_Page: {obj?.Show_On_This_Page}");
+ }
+ }
+
+ Console.WriteLine($"DEBUG: Total objects found: {result.Count}");
+ return result;
+ }
+
+ ///
+ /// Calcula el bounding box de un conjunto de objetos usando coordenadas directas
+ ///
+ private ScreenshotArea CalculateObjectsBoundingBox(List objects)
+ {
+ if (!objects.Any())
+ throw new ArgumentException("La lista de objetos no puede estar vacía");
+
+ float left = float.MaxValue;
+ float top = float.MaxValue;
+ float right = float.MinValue;
+ float bottom = float.MinValue;
+
+ foreach (var obj in objects)
+ {
+ // Usar coordenadas directas del objeto (ya están en metros)
+ float objLeft = obj.Left;
+ float objTop = obj.Top;
+ float objRight = obj.Left + obj.Ancho;
+ float objBottom = obj.Top + obj.Alto;
+
+ left = Math.Min(left, objLeft);
+ top = Math.Min(top, objTop);
+ right = Math.Max(right, objRight);
+ bottom = Math.Max(bottom, objBottom);
+
+ Console.WriteLine($"DEBUG: Object {obj.Id.Value} bounds - L:{objLeft} T:{objTop} R:{objRight} B:{objBottom}");
+ }
+
+ if (left == float.MaxValue) // No se encontraron objetos válidos
+ throw new InvalidOperationException("No se encontraron objetos válidos para calcular el bounding box");
+
+ Console.WriteLine($"DEBUG: Final bounding box - L:{left} T:{top} W:{right - left} H:{bottom - top}");
+
+ return new ScreenshotArea
+ {
+ Left = left,
+ Top = top,
+ Width = right - left,
+ Height = bottom - top
+ };
+ }
+
+ ///
+ /// Captura un área específica del canvas
+ ///
+ private RenderTargetBitmap CaptureCanvasArea(ScreenshotArea area, bool includeBackground)
+ {
+ // Asegurar que el canvas esté actualizado
+ _canvas.UpdateLayout();
+
+ // Convertir área de metros a píxeles
+ var pixelLeft = PixelToMeter.Instance.calc.MetersToPixels(area.Left);
+ var pixelTop = PixelToMeter.Instance.calc.MetersToPixels(area.Top);
+ var pixelWidth = PixelToMeter.Instance.calc.MetersToPixels(area.Width);
+ var pixelHeight = PixelToMeter.Instance.calc.MetersToPixels(area.Height);
+
+ var captureRect = new Rect(pixelLeft, pixelTop, pixelWidth, pixelHeight);
+
+ // Validar que el área esté dentro del canvas
+ var canvasRect = new Rect(0, 0, _canvas.ActualWidth, _canvas.ActualHeight);
+ if (!canvasRect.IntersectsWith(captureRect))
+ throw new ArgumentException("El área especificada está fuera del canvas");
+
+ // Intersectar con el canvas para evitar áreas fuera de límites
+ captureRect.Intersect(canvasRect);
+
+ return CaptureCanvasRect(captureRect, includeBackground);
+ }
+
+ ///
+ /// Captura todo el canvas
+ ///
+ private RenderTargetBitmap CaptureEntireCanvas(bool includeBackground)
+ {
+ _canvas.UpdateLayout();
+ var canvasRect = new Rect(0, 0, _canvas.ActualWidth, _canvas.ActualHeight);
+ return CaptureCanvasRect(canvasRect, includeBackground);
+ }
+
+ ///
+ /// Captura un rectángulo específico del canvas en coordenadas de píxeles
+ ///
+ private RenderTargetBitmap CaptureCanvasRect(Rect captureRect, bool includeBackground)
+ {
+ if (captureRect.Width <= 0 || captureRect.Height <= 0)
+ throw new ArgumentException("Las dimensiones de captura deben ser mayores que 0");
+
+ // Configurar escala para alta calidad
+ var scaleFactor = 2.0; // Factor de escala para mejor calidad
+ var renderWidth = Math.Max(1, (int)(captureRect.Width * scaleFactor));
+ var renderHeight = Math.Max(1, (int)(captureRect.Height * scaleFactor));
+ var dpi = 96 * scaleFactor;
+
+ var renderBitmap = new RenderTargetBitmap(
+ renderWidth,
+ renderHeight,
+ dpi,
+ dpi,
+ PixelFormats.Pbgra32);
+
+ // Crear canvas temporal para renderizado
+ var tempCanvas = new Canvas()
+ {
+ Width = captureRect.Width * scaleFactor,
+ Height = captureRect.Height * scaleFactor,
+ Background = includeBackground ? _canvas.Background : Brushes.White
+ };
+
+ tempCanvas.RenderTransform = new ScaleTransform(scaleFactor, scaleFactor);
+
+ // Clonar elementos visibles que intersectan con el área de captura
+ foreach (UIElement child in _canvas.Children)
+ {
+ if (child.Visibility == Visibility.Visible)
+ {
+ try
+ {
+ var left = Canvas.GetLeft(child);
+ var top = Canvas.GetTop(child);
+
+ var elementRect = new Rect(
+ double.IsNaN(left) ? 0 : left,
+ double.IsNaN(top) ? 0 : top,
+ child.RenderSize.Width,
+ child.RenderSize.Height);
+
+ if (captureRect.IntersectsWith(elementRect))
+ {
+ var visualBrush = new VisualBrush(child)
+ {
+ Stretch = Stretch.None,
+ AlignmentX = AlignmentX.Left,
+ AlignmentY = AlignmentY.Top
+ };
+
+ var rect = new Rectangle()
+ {
+ Width = child.RenderSize.Width * scaleFactor,
+ Height = child.RenderSize.Height * scaleFactor,
+ Fill = visualBrush
+ };
+
+ Canvas.SetLeft(rect, (elementRect.X - captureRect.X) * scaleFactor);
+ Canvas.SetTop(rect, (elementRect.Y - captureRect.Y) * scaleFactor);
+
+ tempCanvas.Children.Add(rect);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[ScreenshotManager] Error procesando elemento: {ex.Message}");
+ }
+ }
+ }
+
+ // Renderizar canvas temporal
+ var scaledSize = new Size(captureRect.Width * scaleFactor, captureRect.Height * scaleFactor);
+ tempCanvas.Measure(scaledSize);
+ tempCanvas.Arrange(new Rect(0, 0, scaledSize.Width, scaledSize.Height));
+ tempCanvas.UpdateLayout();
+
+ renderBitmap.Render(tempCanvas);
+ return renderBitmap;
+ }
+
+ ///
+ /// Procesa la salida del screenshot (guardar archivo y/o generar base64)
+ ///
+ private ScreenshotResult ProcessScreenshotOutput(ScreenshotResult result, string filename, bool saveToFile, bool returnBase64)
+ {
+ try
+ {
+ // Generar nombre de archivo si no se proporcionó
+ if (string.IsNullOrEmpty(filename))
+ {
+ var typePrefix = result.CaptureType.ToString().ToLower();
+ filename = $"{typePrefix}_screenshot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
+ }
+
+ // Asegurar extensión .png
+ if (!filename.ToLower().EndsWith(".png"))
+ filename += ".png";
+
+ // Guardar archivo si se solicita
+ if (saveToFile)
+ {
+ Directory.CreateDirectory(_defaultScreenshotsDirectory);
+ var fullPath = System.IO.Path.IsPathRooted(filename) ? filename : System.IO.Path.Combine(_defaultScreenshotsDirectory, filename);
+
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(result.Bitmap));
+
+ using (var fileStream = new FileStream(fullPath, FileMode.Create))
+ {
+ encoder.Save(fileStream);
+ }
+
+ var fileInfo = new FileInfo(fullPath);
+ result.FilePath = fullPath;
+ result.FileName = System.IO.Path.GetFileName(fullPath);
+ result.Directory = System.IO.Path.GetDirectoryName(fullPath);
+ result.FileSizeBytes = fileInfo.Length;
+ }
+
+ // Generar base64 si se solicita
+ if (returnBase64)
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(result.Bitmap));
+ encoder.Save(memoryStream);
+ result.Base64Data = Convert.ToBase64String(memoryStream.ToArray());
+ }
+ }
+
+ result.Timestamp = DateTime.Now;
+ return result;
+ }
+ catch (Exception ex)
+ {
+ result.Success = false;
+ result.ErrorMessage = $"Error procesando salida: {ex.Message}";
+ result.ErrorType = ex.GetType().Name;
+ return result;
+ }
+ }
+
+ #endregion
+ }
+
+ #region Data Classes
+
+ ///
+ /// Resultado de una operación de screenshot
+ ///
+ public class ScreenshotResult
+ {
+ public bool Success { get; set; }
+ public string ErrorMessage { get; set; }
+ public string ErrorType { get; set; }
+ public RenderTargetBitmap Bitmap { get; set; }
+ public ScreenshotType CaptureType { get; set; }
+ public string FilePath { get; set; }
+ public string FileName { get; set; }
+ public string Directory { get; set; }
+ public long FileSizeBytes { get; set; }
+ public string Base64Data { get; set; }
+ public DateTime Timestamp { get; set; }
+ public bool IncludeBackground { get; set; }
+ public List CapturedObjects { get; set; } = new List();
+ public AreaInfo BoundingBox { get; set; }
+ public AreaInfo CaptureArea { get; set; }
+ public float PaddingMeters { get; set; }
+ }
+
+ ///
+ /// Información de un objeto capturado
+ ///
+ public class CapturedObjectInfo
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Type { get; set; }
+ public float Left { get; set; }
+ public float Top { get; set; }
+ public float Width { get; set; }
+ public float Height { get; set; }
+ public float CenterX { get; set; }
+ public float CenterY { get; set; }
+ }
+
+ ///
+ /// Información de un área
+ ///
+ public class AreaInfo
+ {
+ public float Left { get; set; }
+ public float Top { get; set; }
+ public float Width { get; set; }
+ public float Height { get; set; }
+ public float CenterX { get; set; }
+ public float CenterY { get; set; }
+ }
+
+ ///
+ /// Área de screenshot en coordenadas de metros
+ ///
+ internal class ScreenshotArea
+ {
+ public float Left { get; set; }
+ public float Top { get; set; }
+ public float Width { get; set; }
+ public float Height { get; set; }
+ }
+
+ ///
+ /// Tipos de captura de screenshot
+ ///
+ public enum ScreenshotType
+ {
+ Objects,
+ Area,
+ FullCanvas
+ }
+
+ #endregion
+}
diff --git a/analyze_screenshots.py b/analyze_screenshots.py
index 7fa6555..5091b4f 100644
--- a/analyze_screenshots.py
+++ b/analyze_screenshots.py
@@ -276,7 +276,9 @@ def analyze_all_screenshots(screenshots_dir):
if __name__ == "__main__":
# Directorio donde están los screenshots
- screenshots_dir = r"C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giorgio in Bosco\SimCtrEditor\screenshots"
+ screenshots_dir = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "screenshots"
+ )
if not os.path.exists(screenshots_dir):
print(f"ERROR: El directorio {screenshots_dir} no existe")
diff --git a/test_python_execution.py b/test_python_execution.py
deleted file mode 100644
index 1240a72..0000000
--- a/test_python_execution.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test script to verify Python execution capability in CtrEditor via MCP
-"""
-
-import json
-import socket
-import time
-
-
-def send_mcp_request(host="localhost", port=5006, method="tools/call", params=None):
- """Send MCP request to CtrEditor"""
- try:
- # Create socket connection
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.settimeout(10)
- sock.connect((host, port))
-
- # Prepare request
- request = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or {}}
-
- # Send request
- message = json.dumps(request) + "\n"
- sock.send(message.encode("utf-8"))
-
- # Receive response
- response_data = ""
- while True:
- chunk = sock.recv(4096).decode("utf-8")
- if not chunk:
- break
- response_data += chunk
- if "\n" in response_data:
- break
-
- sock.close()
-
- # Parse response
- if response_data.strip():
- return json.loads(response_data.strip())
- return None
-
- except Exception as e:
- print(f"Error sending request: {e}")
- return None
-
-
-def test_python_execution():
- """Test Python execution functionality"""
- print("Testing Python execution in CtrEditor...")
-
- # Test 1: Simple Python code
- print("\n1. Testing simple Python execution...")
- params = {
- "name": "execute_python",
- "arguments": {
- "code": "result = 2 + 2\nprint(f'Result: {result}')",
- "return_variables": ["result"],
- },
- }
-
- response = send_mcp_request(params=params)
- if response:
- print(f"Response: {json.dumps(response, indent=2)}")
- else:
- print("No response received")
-
- # Test 2: Access CtrEditor objects
- print("\n2. Testing CtrEditor object access...")
- params = {
- "name": "execute_python",
- "arguments": {
- "code": """
-# Access CtrEditor objects
-object_count = len(objects) if objects else 0
-canvas_info = f"Canvas size: {canvas.Width}x{canvas.Height}" if canvas else "No canvas"
-app_info = f"App type: {type(app).__name__}" if app else "No app"
-
-print(f"Object count: {object_count}")
-print(canvas_info)
-print(app_info)
-""",
- "return_variables": ["object_count", "canvas_info", "app_info"],
- },
- }
-
- response = send_mcp_request(params=params)
- if response:
- print(f"Response: {json.dumps(response, indent=2)}")
- else:
- print("No response received")
-
- # Test 3: Get Python help
- print("\n3. Testing Python help...")
- params = {"name": "python_help", "arguments": {}}
-
- response = send_mcp_request(params=params)
- if response:
- print(f"Response: {json.dumps(response, indent=2)}")
- else:
- print("No response received")
-
-
-if __name__ == "__main__":
- test_python_execution()